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