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