162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci#include <errno.h> 362306a36Sopenharmony_ci#include <string.h> 462306a36Sopenharmony_ci#include "../../../util/kvm-stat.h" 562306a36Sopenharmony_ci#include "../../../util/evsel.h" 662306a36Sopenharmony_ci#include <asm/svm.h> 762306a36Sopenharmony_ci#include <asm/vmx.h> 862306a36Sopenharmony_ci#include <asm/kvm.h> 962306a36Sopenharmony_ci 1062306a36Sopenharmony_cidefine_exit_reasons_table(vmx_exit_reasons, VMX_EXIT_REASONS); 1162306a36Sopenharmony_cidefine_exit_reasons_table(svm_exit_reasons, SVM_EXIT_REASONS); 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_cistatic struct kvm_events_ops exit_events = { 1462306a36Sopenharmony_ci .is_begin_event = exit_event_begin, 1562306a36Sopenharmony_ci .is_end_event = exit_event_end, 1662306a36Sopenharmony_ci .decode_key = exit_event_decode_key, 1762306a36Sopenharmony_ci .name = "VM-EXIT" 1862306a36Sopenharmony_ci}; 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ciconst char *vcpu_id_str = "vcpu_id"; 2162306a36Sopenharmony_ciconst char *kvm_exit_reason = "exit_reason"; 2262306a36Sopenharmony_ciconst char *kvm_entry_trace = "kvm:kvm_entry"; 2362306a36Sopenharmony_ciconst char *kvm_exit_trace = "kvm:kvm_exit"; 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci/* 2662306a36Sopenharmony_ci * For the mmio events, we treat: 2762306a36Sopenharmony_ci * the time of MMIO write: kvm_mmio(KVM_TRACE_MMIO_WRITE...) -> kvm_entry 2862306a36Sopenharmony_ci * the time of MMIO read: kvm_exit -> kvm_mmio(KVM_TRACE_MMIO_READ...). 2962306a36Sopenharmony_ci */ 3062306a36Sopenharmony_cistatic void mmio_event_get_key(struct evsel *evsel, struct perf_sample *sample, 3162306a36Sopenharmony_ci struct event_key *key) 3262306a36Sopenharmony_ci{ 3362306a36Sopenharmony_ci key->key = evsel__intval(evsel, sample, "gpa"); 3462306a36Sopenharmony_ci key->info = evsel__intval(evsel, sample, "type"); 3562306a36Sopenharmony_ci} 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#define KVM_TRACE_MMIO_READ_UNSATISFIED 0 3862306a36Sopenharmony_ci#define KVM_TRACE_MMIO_READ 1 3962306a36Sopenharmony_ci#define KVM_TRACE_MMIO_WRITE 2 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistatic bool mmio_event_begin(struct evsel *evsel, 4262306a36Sopenharmony_ci struct perf_sample *sample, struct event_key *key) 4362306a36Sopenharmony_ci{ 4462306a36Sopenharmony_ci /* MMIO read begin event in kernel. */ 4562306a36Sopenharmony_ci if (kvm_exit_event(evsel)) 4662306a36Sopenharmony_ci return true; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci /* MMIO write begin event in kernel. */ 4962306a36Sopenharmony_ci if (evsel__name_is(evsel, "kvm:kvm_mmio") && 5062306a36Sopenharmony_ci evsel__intval(evsel, sample, "type") == KVM_TRACE_MMIO_WRITE) { 5162306a36Sopenharmony_ci mmio_event_get_key(evsel, sample, key); 5262306a36Sopenharmony_ci return true; 5362306a36Sopenharmony_ci } 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci return false; 5662306a36Sopenharmony_ci} 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cistatic bool mmio_event_end(struct evsel *evsel, struct perf_sample *sample, 5962306a36Sopenharmony_ci struct event_key *key) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci /* MMIO write end event in kernel. */ 6262306a36Sopenharmony_ci if (kvm_entry_event(evsel)) 6362306a36Sopenharmony_ci return true; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci /* MMIO read end event in kernel.*/ 6662306a36Sopenharmony_ci if (evsel__name_is(evsel, "kvm:kvm_mmio") && 6762306a36Sopenharmony_ci evsel__intval(evsel, sample, "type") == KVM_TRACE_MMIO_READ) { 6862306a36Sopenharmony_ci mmio_event_get_key(evsel, sample, key); 6962306a36Sopenharmony_ci return true; 7062306a36Sopenharmony_ci } 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci return false; 7362306a36Sopenharmony_ci} 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic void mmio_event_decode_key(struct perf_kvm_stat *kvm __maybe_unused, 7662306a36Sopenharmony_ci struct event_key *key, 7762306a36Sopenharmony_ci char *decode) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci scnprintf(decode, KVM_EVENT_NAME_LEN, "%#lx:%s", 8062306a36Sopenharmony_ci (unsigned long)key->key, 8162306a36Sopenharmony_ci key->info == KVM_TRACE_MMIO_WRITE ? "W" : "R"); 8262306a36Sopenharmony_ci} 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistatic struct kvm_events_ops mmio_events = { 8562306a36Sopenharmony_ci .is_begin_event = mmio_event_begin, 8662306a36Sopenharmony_ci .is_end_event = mmio_event_end, 8762306a36Sopenharmony_ci .decode_key = mmio_event_decode_key, 8862306a36Sopenharmony_ci .name = "MMIO Access" 8962306a36Sopenharmony_ci}; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci /* The time of emulation pio access is from kvm_pio to kvm_entry. */ 9262306a36Sopenharmony_cistatic void ioport_event_get_key(struct evsel *evsel, 9362306a36Sopenharmony_ci struct perf_sample *sample, 9462306a36Sopenharmony_ci struct event_key *key) 9562306a36Sopenharmony_ci{ 9662306a36Sopenharmony_ci key->key = evsel__intval(evsel, sample, "port"); 9762306a36Sopenharmony_ci key->info = evsel__intval(evsel, sample, "rw"); 9862306a36Sopenharmony_ci} 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_cistatic bool ioport_event_begin(struct evsel *evsel, 10162306a36Sopenharmony_ci struct perf_sample *sample, 10262306a36Sopenharmony_ci struct event_key *key) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci if (evsel__name_is(evsel, "kvm:kvm_pio")) { 10562306a36Sopenharmony_ci ioport_event_get_key(evsel, sample, key); 10662306a36Sopenharmony_ci return true; 10762306a36Sopenharmony_ci } 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci return false; 11062306a36Sopenharmony_ci} 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cistatic bool ioport_event_end(struct evsel *evsel, 11362306a36Sopenharmony_ci struct perf_sample *sample __maybe_unused, 11462306a36Sopenharmony_ci struct event_key *key __maybe_unused) 11562306a36Sopenharmony_ci{ 11662306a36Sopenharmony_ci return kvm_entry_event(evsel); 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistatic void ioport_event_decode_key(struct perf_kvm_stat *kvm __maybe_unused, 12062306a36Sopenharmony_ci struct event_key *key, 12162306a36Sopenharmony_ci char *decode) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci scnprintf(decode, KVM_EVENT_NAME_LEN, "%#llx:%s", 12462306a36Sopenharmony_ci (unsigned long long)key->key, 12562306a36Sopenharmony_ci key->info ? "POUT" : "PIN"); 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cistatic struct kvm_events_ops ioport_events = { 12962306a36Sopenharmony_ci .is_begin_event = ioport_event_begin, 13062306a36Sopenharmony_ci .is_end_event = ioport_event_end, 13162306a36Sopenharmony_ci .decode_key = ioport_event_decode_key, 13262306a36Sopenharmony_ci .name = "IO Port Access" 13362306a36Sopenharmony_ci}; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci /* The time of emulation msr is from kvm_msr to kvm_entry. */ 13662306a36Sopenharmony_cistatic void msr_event_get_key(struct evsel *evsel, 13762306a36Sopenharmony_ci struct perf_sample *sample, 13862306a36Sopenharmony_ci struct event_key *key) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci key->key = evsel__intval(evsel, sample, "ecx"); 14162306a36Sopenharmony_ci key->info = evsel__intval(evsel, sample, "write"); 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic bool msr_event_begin(struct evsel *evsel, 14562306a36Sopenharmony_ci struct perf_sample *sample, 14662306a36Sopenharmony_ci struct event_key *key) 14762306a36Sopenharmony_ci{ 14862306a36Sopenharmony_ci if (evsel__name_is(evsel, "kvm:kvm_msr")) { 14962306a36Sopenharmony_ci msr_event_get_key(evsel, sample, key); 15062306a36Sopenharmony_ci return true; 15162306a36Sopenharmony_ci } 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci return false; 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_cistatic bool msr_event_end(struct evsel *evsel, 15762306a36Sopenharmony_ci struct perf_sample *sample __maybe_unused, 15862306a36Sopenharmony_ci struct event_key *key __maybe_unused) 15962306a36Sopenharmony_ci{ 16062306a36Sopenharmony_ci return kvm_entry_event(evsel); 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_cistatic void msr_event_decode_key(struct perf_kvm_stat *kvm __maybe_unused, 16462306a36Sopenharmony_ci struct event_key *key, 16562306a36Sopenharmony_ci char *decode) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci scnprintf(decode, KVM_EVENT_NAME_LEN, "%#llx:%s", 16862306a36Sopenharmony_ci (unsigned long long)key->key, 16962306a36Sopenharmony_ci key->info ? "W" : "R"); 17062306a36Sopenharmony_ci} 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_cistatic struct kvm_events_ops msr_events = { 17362306a36Sopenharmony_ci .is_begin_event = msr_event_begin, 17462306a36Sopenharmony_ci .is_end_event = msr_event_end, 17562306a36Sopenharmony_ci .decode_key = msr_event_decode_key, 17662306a36Sopenharmony_ci .name = "MSR Access" 17762306a36Sopenharmony_ci}; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ciconst char *kvm_events_tp[] = { 18062306a36Sopenharmony_ci "kvm:kvm_entry", 18162306a36Sopenharmony_ci "kvm:kvm_exit", 18262306a36Sopenharmony_ci "kvm:kvm_mmio", 18362306a36Sopenharmony_ci "kvm:kvm_pio", 18462306a36Sopenharmony_ci "kvm:kvm_msr", 18562306a36Sopenharmony_ci NULL, 18662306a36Sopenharmony_ci}; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_cistruct kvm_reg_events_ops kvm_reg_events_ops[] = { 18962306a36Sopenharmony_ci { .name = "vmexit", .ops = &exit_events }, 19062306a36Sopenharmony_ci { .name = "mmio", .ops = &mmio_events }, 19162306a36Sopenharmony_ci { .name = "ioport", .ops = &ioport_events }, 19262306a36Sopenharmony_ci { .name = "msr", .ops = &msr_events }, 19362306a36Sopenharmony_ci { NULL, NULL }, 19462306a36Sopenharmony_ci}; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ciconst char * const kvm_skip_events[] = { 19762306a36Sopenharmony_ci "HLT", 19862306a36Sopenharmony_ci NULL, 19962306a36Sopenharmony_ci}; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ciint cpu_isa_init(struct perf_kvm_stat *kvm, const char *cpuid) 20262306a36Sopenharmony_ci{ 20362306a36Sopenharmony_ci if (strstr(cpuid, "Intel")) { 20462306a36Sopenharmony_ci kvm->exit_reasons = vmx_exit_reasons; 20562306a36Sopenharmony_ci kvm->exit_reasons_isa = "VMX"; 20662306a36Sopenharmony_ci } else if (strstr(cpuid, "AMD") || strstr(cpuid, "Hygon")) { 20762306a36Sopenharmony_ci kvm->exit_reasons = svm_exit_reasons; 20862306a36Sopenharmony_ci kvm->exit_reasons_isa = "SVM"; 20962306a36Sopenharmony_ci } else 21062306a36Sopenharmony_ci return -ENOTSUP; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci return 0; 21362306a36Sopenharmony_ci} 214