1 // SPDX-License-Identifier: GPL-2.0 2 #include <errno.h> 3 #include <string.h> 4 #include "../kvm-stat.h" 5 #include "../evsel.h" 6 #include "../env.h" 7 #include "../../arch/x86/include/uapi/asm/svm.h" 8 #include "../../arch/x86/include/uapi/asm/vmx.h" 9 #include "../../arch/x86/include/uapi/asm/kvm.h" 10 #include <subcmd/parse-options.h> 11 12 define_exit_reasons_table(vmx_exit_reasons, VMX_EXIT_REASONS); 13 define_exit_reasons_table(svm_exit_reasons, SVM_EXIT_REASONS); 14 15 static const struct kvm_events_ops exit_events = { 16 .is_begin_event = exit_event_begin, 17 .is_end_event = exit_event_end, 18 .decode_key = exit_event_decode_key, 19 .name = "VM-EXIT" 20 }; 21 22 /* 23 * For the mmio events, we treat: 24 * the time of MMIO write: kvm_mmio(KVM_TRACE_MMIO_WRITE...) -> kvm_entry 25 * the time of MMIO read: kvm_exit -> kvm_mmio(KVM_TRACE_MMIO_READ...). 26 */ 27 static void mmio_event_get_key(struct evsel *evsel, struct perf_sample *sample, 28 struct event_key *key) 29 { 30 key->key = evsel__intval(evsel, sample, "gpa"); 31 key->info = evsel__intval(evsel, sample, "type"); 32 } 33 34 #define KVM_TRACE_MMIO_READ_UNSATISFIED 0 35 #define KVM_TRACE_MMIO_READ 1 36 #define KVM_TRACE_MMIO_WRITE 2 37 38 static bool mmio_event_begin(struct evsel *evsel, 39 struct perf_sample *sample, struct event_key *key) 40 { 41 /* MMIO read begin event in kernel. */ 42 if (kvm_exit_event(evsel)) 43 return true; 44 45 /* MMIO write begin event in kernel. */ 46 if (evsel__name_is(evsel, "kvm:kvm_mmio") && 47 evsel__intval(evsel, sample, "type") == KVM_TRACE_MMIO_WRITE) { 48 mmio_event_get_key(evsel, sample, key); 49 return true; 50 } 51 52 return false; 53 } 54 55 static bool mmio_event_end(struct evsel *evsel, struct perf_sample *sample, 56 struct event_key *key) 57 { 58 /* MMIO write end event in kernel. */ 59 if (kvm_entry_event(evsel)) 60 return true; 61 62 /* MMIO read end event in kernel.*/ 63 if (evsel__name_is(evsel, "kvm:kvm_mmio") && 64 evsel__intval(evsel, sample, "type") == KVM_TRACE_MMIO_READ) { 65 mmio_event_get_key(evsel, sample, key); 66 return true; 67 } 68 69 return false; 70 } 71 72 static void mmio_event_decode_key(struct perf_kvm_stat *kvm __maybe_unused, 73 struct event_key *key, 74 char *decode) 75 { 76 scnprintf(decode, KVM_EVENT_NAME_LEN, "%#lx:%s", 77 (unsigned long)key->key, 78 key->info == KVM_TRACE_MMIO_WRITE ? "W" : "R"); 79 } 80 81 static const struct kvm_events_ops mmio_events = { 82 .is_begin_event = mmio_event_begin, 83 .is_end_event = mmio_event_end, 84 .decode_key = mmio_event_decode_key, 85 .name = "MMIO Access" 86 }; 87 88 /* The time of emulation pio access is from kvm_pio to kvm_entry. */ 89 static void ioport_event_get_key(struct evsel *evsel, 90 struct perf_sample *sample, 91 struct event_key *key) 92 { 93 key->key = evsel__intval(evsel, sample, "port"); 94 key->info = evsel__intval(evsel, sample, "rw"); 95 } 96 97 static bool ioport_event_begin(struct evsel *evsel, 98 struct perf_sample *sample, 99 struct event_key *key) 100 { 101 if (evsel__name_is(evsel, "kvm:kvm_pio")) { 102 ioport_event_get_key(evsel, sample, key); 103 return true; 104 } 105 106 return false; 107 } 108 109 static bool ioport_event_end(struct evsel *evsel, 110 struct perf_sample *sample __maybe_unused, 111 struct event_key *key __maybe_unused) 112 { 113 return kvm_entry_event(evsel); 114 } 115 116 static void ioport_event_decode_key(struct perf_kvm_stat *kvm __maybe_unused, 117 struct event_key *key, 118 char *decode) 119 { 120 scnprintf(decode, KVM_EVENT_NAME_LEN, "%#llx:%s", 121 (unsigned long long)key->key, 122 key->info ? "POUT" : "PIN"); 123 } 124 125 static const struct kvm_events_ops ioport_events = { 126 .is_begin_event = ioport_event_begin, 127 .is_end_event = ioport_event_end, 128 .decode_key = ioport_event_decode_key, 129 .name = "IO Port Access" 130 }; 131 132 /* The time of emulation msr is from kvm_msr to kvm_entry. */ 133 static void msr_event_get_key(struct evsel *evsel, 134 struct perf_sample *sample, 135 struct event_key *key) 136 { 137 key->key = evsel__intval(evsel, sample, "ecx"); 138 key->info = evsel__intval(evsel, sample, "write"); 139 } 140 141 static bool msr_event_begin(struct evsel *evsel, 142 struct perf_sample *sample, 143 struct event_key *key) 144 { 145 if (evsel__name_is(evsel, "kvm:kvm_msr")) { 146 msr_event_get_key(evsel, sample, key); 147 return true; 148 } 149 150 return false; 151 } 152 153 static bool msr_event_end(struct evsel *evsel, 154 struct perf_sample *sample __maybe_unused, 155 struct event_key *key __maybe_unused) 156 { 157 return kvm_entry_event(evsel); 158 } 159 160 static void msr_event_decode_key(struct perf_kvm_stat *kvm __maybe_unused, 161 struct event_key *key, 162 char *decode) 163 { 164 scnprintf(decode, KVM_EVENT_NAME_LEN, "%#llx:%s", 165 (unsigned long long)key->key, 166 key->info ? "W" : "R"); 167 } 168 169 static const struct kvm_events_ops msr_events = { 170 .is_begin_event = msr_event_begin, 171 .is_end_event = msr_event_end, 172 .decode_key = msr_event_decode_key, 173 .name = "MSR Access" 174 }; 175 176 static const char * const __kvm_events_tp[] = { 177 "kvm:kvm_entry", 178 "kvm:kvm_exit", 179 "kvm:kvm_mmio", 180 "kvm:kvm_pio", 181 "kvm:kvm_msr", 182 NULL, 183 }; 184 185 static const struct kvm_reg_events_ops __kvm_reg_events_ops[] = { 186 { .name = "vmexit", .ops = &exit_events }, 187 { .name = "mmio", .ops = &mmio_events }, 188 { .name = "ioport", .ops = &ioport_events }, 189 { .name = "msr", .ops = &msr_events }, 190 { NULL, NULL }, 191 }; 192 193 static const char * const __kvm_skip_events[] = { 194 "HLT", 195 NULL, 196 }; 197 198 int __cpu_isa_init_x86(struct perf_kvm_stat *kvm, const char *cpuid) 199 { 200 if (strstr(cpuid, "Intel")) { 201 kvm->exit_reasons = vmx_exit_reasons; 202 kvm->exit_reasons_isa = "VMX"; 203 } else if (strstr(cpuid, "AMD") || strstr(cpuid, "Hygon")) { 204 kvm->exit_reasons = svm_exit_reasons; 205 kvm->exit_reasons_isa = "SVM"; 206 } else 207 return -ENOTSUP; 208 209 return 0; 210 } 211 212 /* 213 * After KVM supports PEBS for guest on Intel platforms 214 * (https://lore.kernel.org/all/20220411101946.20262-1-likexu@tencent.com/), 215 * host loses the capability to sample guest with PEBS since all PEBS related 216 * MSRs are switched to guest value after vm-entry, like IA32_DS_AREA MSR is 217 * switched to guest GVA at vm-entry. This would lead to "perf kvm record" 218 * fails to sample guest on Intel platforms since "cycles:P" event is used to 219 * sample guest by default. 220 * 221 * So, to avoid this issue explicitly use "cycles" instead of "cycles:P" event 222 * by default to sample guest on Intel platforms. 223 */ 224 int __kvm_add_default_arch_event_x86(int *argc, const char **argv) 225 { 226 const char **tmp; 227 bool event = false; 228 int ret = 0, i, j = *argc; 229 230 const struct option event_options[] = { 231 OPT_BOOLEAN('e', "event", &event, NULL), 232 OPT_BOOLEAN(0, "pfm-events", &event, NULL), 233 OPT_END() 234 }; 235 236 if (!x86__is_intel_cpu()) 237 return 0; 238 239 tmp = calloc(j + 1, sizeof(char *)); 240 if (!tmp) 241 return -ENOMEM; 242 243 for (i = 0; i < j; i++) 244 tmp[i] = argv[i]; 245 246 parse_options(j, tmp, event_options, NULL, PARSE_OPT_KEEP_UNKNOWN); 247 if (!event) { 248 argv[j++] = STRDUP_FAIL_EXIT("-e"); 249 argv[j++] = STRDUP_FAIL_EXIT("cycles"); 250 *argc += 2; 251 } 252 253 free(tmp); 254 return 0; 255 256 EXIT: 257 free(tmp); 258 return ret; 259 } 260 261 const char * const *__kvm_events_tp_x86(void) 262 { 263 return __kvm_events_tp; 264 } 265 266 const struct kvm_reg_events_ops *__kvm_reg_events_ops_x86(void) 267 { 268 return __kvm_reg_events_ops; 269 } 270 271 const char * const *__kvm_skip_events_x86(void) 272 { 273 return __kvm_skip_events; 274 } 275