1 // SPDX-License-Identifier: GPL-2.0 2 #include <errno.h> 3 #include "../../util/kvm-stat.h" 4 #include <asm/svm.h> 5 #include <asm/vmx.h> 6 #include <asm/kvm.h> 7 8 define_exit_reasons_table(vmx_exit_reasons, VMX_EXIT_REASONS); 9 define_exit_reasons_table(svm_exit_reasons, SVM_EXIT_REASONS); 10 11 static struct kvm_events_ops exit_events = { 12 .is_begin_event = exit_event_begin, 13 .is_end_event = exit_event_end, 14 .decode_key = exit_event_decode_key, 15 .name = "VM-EXIT" 16 }; 17 18 const char *vcpu_id_str = "vcpu_id"; 19 const int decode_str_len = 20; 20 const char *kvm_exit_reason = "exit_reason"; 21 const char *kvm_entry_trace = "kvm:kvm_entry"; 22 const char *kvm_exit_trace = "kvm:kvm_exit"; 23 24 /* 25 * For the mmio events, we treat: 26 * the time of MMIO write: kvm_mmio(KVM_TRACE_MMIO_WRITE...) -> kvm_entry 27 * the time of MMIO read: kvm_exit -> kvm_mmio(KVM_TRACE_MMIO_READ...). 28 */ 29 static void mmio_event_get_key(struct perf_evsel *evsel, struct perf_sample *sample, 30 struct event_key *key) 31 { 32 key->key = perf_evsel__intval(evsel, sample, "gpa"); 33 key->info = perf_evsel__intval(evsel, sample, "type"); 34 } 35 36 #define KVM_TRACE_MMIO_READ_UNSATISFIED 0 37 #define KVM_TRACE_MMIO_READ 1 38 #define KVM_TRACE_MMIO_WRITE 2 39 40 static bool mmio_event_begin(struct perf_evsel *evsel, 41 struct perf_sample *sample, struct event_key *key) 42 { 43 /* MMIO read begin event in kernel. */ 44 if (kvm_exit_event(evsel)) 45 return true; 46 47 /* MMIO write begin event in kernel. */ 48 if (!strcmp(evsel->name, "kvm:kvm_mmio") && 49 perf_evsel__intval(evsel, sample, "type") == KVM_TRACE_MMIO_WRITE) { 50 mmio_event_get_key(evsel, sample, key); 51 return true; 52 } 53 54 return false; 55 } 56 57 static bool mmio_event_end(struct perf_evsel *evsel, struct perf_sample *sample, 58 struct event_key *key) 59 { 60 /* MMIO write end event in kernel. */ 61 if (kvm_entry_event(evsel)) 62 return true; 63 64 /* MMIO read end event in kernel.*/ 65 if (!strcmp(evsel->name, "kvm:kvm_mmio") && 66 perf_evsel__intval(evsel, sample, "type") == KVM_TRACE_MMIO_READ) { 67 mmio_event_get_key(evsel, sample, key); 68 return true; 69 } 70 71 return false; 72 } 73 74 static void mmio_event_decode_key(struct perf_kvm_stat *kvm __maybe_unused, 75 struct event_key *key, 76 char *decode) 77 { 78 scnprintf(decode, decode_str_len, "%#lx:%s", 79 (unsigned long)key->key, 80 key->info == KVM_TRACE_MMIO_WRITE ? "W" : "R"); 81 } 82 83 static struct kvm_events_ops mmio_events = { 84 .is_begin_event = mmio_event_begin, 85 .is_end_event = mmio_event_end, 86 .decode_key = mmio_event_decode_key, 87 .name = "MMIO Access" 88 }; 89 90 /* The time of emulation pio access is from kvm_pio to kvm_entry. */ 91 static void ioport_event_get_key(struct perf_evsel *evsel, 92 struct perf_sample *sample, 93 struct event_key *key) 94 { 95 key->key = perf_evsel__intval(evsel, sample, "port"); 96 key->info = perf_evsel__intval(evsel, sample, "rw"); 97 } 98 99 static bool ioport_event_begin(struct perf_evsel *evsel, 100 struct perf_sample *sample, 101 struct event_key *key) 102 { 103 if (!strcmp(evsel->name, "kvm:kvm_pio")) { 104 ioport_event_get_key(evsel, sample, key); 105 return true; 106 } 107 108 return false; 109 } 110 111 static bool ioport_event_end(struct perf_evsel *evsel, 112 struct perf_sample *sample __maybe_unused, 113 struct event_key *key __maybe_unused) 114 { 115 return kvm_entry_event(evsel); 116 } 117 118 static void ioport_event_decode_key(struct perf_kvm_stat *kvm __maybe_unused, 119 struct event_key *key, 120 char *decode) 121 { 122 scnprintf(decode, decode_str_len, "%#llx:%s", 123 (unsigned long long)key->key, 124 key->info ? "POUT" : "PIN"); 125 } 126 127 static struct kvm_events_ops ioport_events = { 128 .is_begin_event = ioport_event_begin, 129 .is_end_event = ioport_event_end, 130 .decode_key = ioport_event_decode_key, 131 .name = "IO Port Access" 132 }; 133 134 const char *kvm_events_tp[] = { 135 "kvm:kvm_entry", 136 "kvm:kvm_exit", 137 "kvm:kvm_mmio", 138 "kvm:kvm_pio", 139 NULL, 140 }; 141 142 struct kvm_reg_events_ops kvm_reg_events_ops[] = { 143 { .name = "vmexit", .ops = &exit_events }, 144 { .name = "mmio", .ops = &mmio_events }, 145 { .name = "ioport", .ops = &ioport_events }, 146 { NULL, NULL }, 147 }; 148 149 const char * const kvm_skip_events[] = { 150 "HLT", 151 NULL, 152 }; 153 154 int cpu_isa_init(struct perf_kvm_stat *kvm, const char *cpuid) 155 { 156 if (strstr(cpuid, "Intel")) { 157 kvm->exit_reasons = vmx_exit_reasons; 158 kvm->exit_reasons_isa = "VMX"; 159 } else if (strstr(cpuid, "AMD") || strstr(cpuid, "Hygon")) { 160 kvm->exit_reasons = svm_exit_reasons; 161 kvm->exit_reasons_isa = "SVM"; 162 } else 163 return -ENOTSUP; 164 165 return 0; 166 } 167