18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci#include <errno.h> 38c2ecf20Sopenharmony_ci#include <string.h> 48c2ecf20Sopenharmony_ci#include "../../../util/kvm-stat.h" 58c2ecf20Sopenharmony_ci#include "../../../util/evsel.h" 68c2ecf20Sopenharmony_ci#include <asm/svm.h> 78c2ecf20Sopenharmony_ci#include <asm/vmx.h> 88c2ecf20Sopenharmony_ci#include <asm/kvm.h> 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_cidefine_exit_reasons_table(vmx_exit_reasons, VMX_EXIT_REASONS); 118c2ecf20Sopenharmony_cidefine_exit_reasons_table(svm_exit_reasons, SVM_EXIT_REASONS); 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_cistatic struct kvm_events_ops exit_events = { 148c2ecf20Sopenharmony_ci .is_begin_event = exit_event_begin, 158c2ecf20Sopenharmony_ci .is_end_event = exit_event_end, 168c2ecf20Sopenharmony_ci .decode_key = exit_event_decode_key, 178c2ecf20Sopenharmony_ci .name = "VM-EXIT" 188c2ecf20Sopenharmony_ci}; 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ciconst char *vcpu_id_str = "vcpu_id"; 218c2ecf20Sopenharmony_ciconst int decode_str_len = 20; 228c2ecf20Sopenharmony_ciconst char *kvm_exit_reason = "exit_reason"; 238c2ecf20Sopenharmony_ciconst char *kvm_entry_trace = "kvm:kvm_entry"; 248c2ecf20Sopenharmony_ciconst char *kvm_exit_trace = "kvm:kvm_exit"; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci/* 278c2ecf20Sopenharmony_ci * For the mmio events, we treat: 288c2ecf20Sopenharmony_ci * the time of MMIO write: kvm_mmio(KVM_TRACE_MMIO_WRITE...) -> kvm_entry 298c2ecf20Sopenharmony_ci * the time of MMIO read: kvm_exit -> kvm_mmio(KVM_TRACE_MMIO_READ...). 308c2ecf20Sopenharmony_ci */ 318c2ecf20Sopenharmony_cistatic void mmio_event_get_key(struct evsel *evsel, struct perf_sample *sample, 328c2ecf20Sopenharmony_ci struct event_key *key) 338c2ecf20Sopenharmony_ci{ 348c2ecf20Sopenharmony_ci key->key = evsel__intval(evsel, sample, "gpa"); 358c2ecf20Sopenharmony_ci key->info = evsel__intval(evsel, sample, "type"); 368c2ecf20Sopenharmony_ci} 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#define KVM_TRACE_MMIO_READ_UNSATISFIED 0 398c2ecf20Sopenharmony_ci#define KVM_TRACE_MMIO_READ 1 408c2ecf20Sopenharmony_ci#define KVM_TRACE_MMIO_WRITE 2 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic bool mmio_event_begin(struct evsel *evsel, 438c2ecf20Sopenharmony_ci struct perf_sample *sample, struct event_key *key) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci /* MMIO read begin event in kernel. */ 468c2ecf20Sopenharmony_ci if (kvm_exit_event(evsel)) 478c2ecf20Sopenharmony_ci return true; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci /* MMIO write begin event in kernel. */ 508c2ecf20Sopenharmony_ci if (!strcmp(evsel->name, "kvm:kvm_mmio") && 518c2ecf20Sopenharmony_ci evsel__intval(evsel, sample, "type") == KVM_TRACE_MMIO_WRITE) { 528c2ecf20Sopenharmony_ci mmio_event_get_key(evsel, sample, key); 538c2ecf20Sopenharmony_ci return true; 548c2ecf20Sopenharmony_ci } 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci return false; 578c2ecf20Sopenharmony_ci} 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic bool mmio_event_end(struct evsel *evsel, struct perf_sample *sample, 608c2ecf20Sopenharmony_ci struct event_key *key) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci /* MMIO write end event in kernel. */ 638c2ecf20Sopenharmony_ci if (kvm_entry_event(evsel)) 648c2ecf20Sopenharmony_ci return true; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci /* MMIO read end event in kernel.*/ 678c2ecf20Sopenharmony_ci if (!strcmp(evsel->name, "kvm:kvm_mmio") && 688c2ecf20Sopenharmony_ci evsel__intval(evsel, sample, "type") == KVM_TRACE_MMIO_READ) { 698c2ecf20Sopenharmony_ci mmio_event_get_key(evsel, sample, key); 708c2ecf20Sopenharmony_ci return true; 718c2ecf20Sopenharmony_ci } 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci return false; 748c2ecf20Sopenharmony_ci} 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistatic void mmio_event_decode_key(struct perf_kvm_stat *kvm __maybe_unused, 778c2ecf20Sopenharmony_ci struct event_key *key, 788c2ecf20Sopenharmony_ci char *decode) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci scnprintf(decode, decode_str_len, "%#lx:%s", 818c2ecf20Sopenharmony_ci (unsigned long)key->key, 828c2ecf20Sopenharmony_ci key->info == KVM_TRACE_MMIO_WRITE ? "W" : "R"); 838c2ecf20Sopenharmony_ci} 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_cistatic struct kvm_events_ops mmio_events = { 868c2ecf20Sopenharmony_ci .is_begin_event = mmio_event_begin, 878c2ecf20Sopenharmony_ci .is_end_event = mmio_event_end, 888c2ecf20Sopenharmony_ci .decode_key = mmio_event_decode_key, 898c2ecf20Sopenharmony_ci .name = "MMIO Access" 908c2ecf20Sopenharmony_ci}; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci /* The time of emulation pio access is from kvm_pio to kvm_entry. */ 938c2ecf20Sopenharmony_cistatic void ioport_event_get_key(struct evsel *evsel, 948c2ecf20Sopenharmony_ci struct perf_sample *sample, 958c2ecf20Sopenharmony_ci struct event_key *key) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci key->key = evsel__intval(evsel, sample, "port"); 988c2ecf20Sopenharmony_ci key->info = evsel__intval(evsel, sample, "rw"); 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistatic bool ioport_event_begin(struct evsel *evsel, 1028c2ecf20Sopenharmony_ci struct perf_sample *sample, 1038c2ecf20Sopenharmony_ci struct event_key *key) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci if (!strcmp(evsel->name, "kvm:kvm_pio")) { 1068c2ecf20Sopenharmony_ci ioport_event_get_key(evsel, sample, key); 1078c2ecf20Sopenharmony_ci return true; 1088c2ecf20Sopenharmony_ci } 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci return false; 1118c2ecf20Sopenharmony_ci} 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_cistatic bool ioport_event_end(struct evsel *evsel, 1148c2ecf20Sopenharmony_ci struct perf_sample *sample __maybe_unused, 1158c2ecf20Sopenharmony_ci struct event_key *key __maybe_unused) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci return kvm_entry_event(evsel); 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic void ioport_event_decode_key(struct perf_kvm_stat *kvm __maybe_unused, 1218c2ecf20Sopenharmony_ci struct event_key *key, 1228c2ecf20Sopenharmony_ci char *decode) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci scnprintf(decode, decode_str_len, "%#llx:%s", 1258c2ecf20Sopenharmony_ci (unsigned long long)key->key, 1268c2ecf20Sopenharmony_ci key->info ? "POUT" : "PIN"); 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistatic struct kvm_events_ops ioport_events = { 1308c2ecf20Sopenharmony_ci .is_begin_event = ioport_event_begin, 1318c2ecf20Sopenharmony_ci .is_end_event = ioport_event_end, 1328c2ecf20Sopenharmony_ci .decode_key = ioport_event_decode_key, 1338c2ecf20Sopenharmony_ci .name = "IO Port Access" 1348c2ecf20Sopenharmony_ci}; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ciconst char *kvm_events_tp[] = { 1378c2ecf20Sopenharmony_ci "kvm:kvm_entry", 1388c2ecf20Sopenharmony_ci "kvm:kvm_exit", 1398c2ecf20Sopenharmony_ci "kvm:kvm_mmio", 1408c2ecf20Sopenharmony_ci "kvm:kvm_pio", 1418c2ecf20Sopenharmony_ci NULL, 1428c2ecf20Sopenharmony_ci}; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_cistruct kvm_reg_events_ops kvm_reg_events_ops[] = { 1458c2ecf20Sopenharmony_ci { .name = "vmexit", .ops = &exit_events }, 1468c2ecf20Sopenharmony_ci { .name = "mmio", .ops = &mmio_events }, 1478c2ecf20Sopenharmony_ci { .name = "ioport", .ops = &ioport_events }, 1488c2ecf20Sopenharmony_ci { NULL, NULL }, 1498c2ecf20Sopenharmony_ci}; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ciconst char * const kvm_skip_events[] = { 1528c2ecf20Sopenharmony_ci "HLT", 1538c2ecf20Sopenharmony_ci NULL, 1548c2ecf20Sopenharmony_ci}; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ciint cpu_isa_init(struct perf_kvm_stat *kvm, const char *cpuid) 1578c2ecf20Sopenharmony_ci{ 1588c2ecf20Sopenharmony_ci if (strstr(cpuid, "Intel")) { 1598c2ecf20Sopenharmony_ci kvm->exit_reasons = vmx_exit_reasons; 1608c2ecf20Sopenharmony_ci kvm->exit_reasons_isa = "VMX"; 1618c2ecf20Sopenharmony_ci } else if (strstr(cpuid, "AMD") || strstr(cpuid, "Hygon")) { 1628c2ecf20Sopenharmony_ci kvm->exit_reasons = svm_exit_reasons; 1638c2ecf20Sopenharmony_ci kvm->exit_reasons_isa = "SVM"; 1648c2ecf20Sopenharmony_ci } else 1658c2ecf20Sopenharmony_ci return -ENOTSUP; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci return 0; 1688c2ecf20Sopenharmony_ci} 169