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