xref: /OK3568_Linux_fs/kernel/tools/perf/arch/x86/util/kvm-stat.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun #include <errno.h>
3*4882a593Smuzhiyun #include <string.h>
4*4882a593Smuzhiyun #include "../../../util/kvm-stat.h"
5*4882a593Smuzhiyun #include "../../../util/evsel.h"
6*4882a593Smuzhiyun #include <asm/svm.h>
7*4882a593Smuzhiyun #include <asm/vmx.h>
8*4882a593Smuzhiyun #include <asm/kvm.h>
9*4882a593Smuzhiyun 
10*4882a593Smuzhiyun define_exit_reasons_table(vmx_exit_reasons, VMX_EXIT_REASONS);
11*4882a593Smuzhiyun define_exit_reasons_table(svm_exit_reasons, SVM_EXIT_REASONS);
12*4882a593Smuzhiyun 
13*4882a593Smuzhiyun static struct kvm_events_ops exit_events = {
14*4882a593Smuzhiyun 	.is_begin_event = exit_event_begin,
15*4882a593Smuzhiyun 	.is_end_event = exit_event_end,
16*4882a593Smuzhiyun 	.decode_key = exit_event_decode_key,
17*4882a593Smuzhiyun 	.name = "VM-EXIT"
18*4882a593Smuzhiyun };
19*4882a593Smuzhiyun 
20*4882a593Smuzhiyun const char *vcpu_id_str = "vcpu_id";
21*4882a593Smuzhiyun const int decode_str_len = 20;
22*4882a593Smuzhiyun const char *kvm_exit_reason = "exit_reason";
23*4882a593Smuzhiyun const char *kvm_entry_trace = "kvm:kvm_entry";
24*4882a593Smuzhiyun const char *kvm_exit_trace = "kvm:kvm_exit";
25*4882a593Smuzhiyun 
26*4882a593Smuzhiyun /*
27*4882a593Smuzhiyun  * For the mmio events, we treat:
28*4882a593Smuzhiyun  * the time of MMIO write: kvm_mmio(KVM_TRACE_MMIO_WRITE...) -> kvm_entry
29*4882a593Smuzhiyun  * the time of MMIO read: kvm_exit -> kvm_mmio(KVM_TRACE_MMIO_READ...).
30*4882a593Smuzhiyun  */
mmio_event_get_key(struct evsel * evsel,struct perf_sample * sample,struct event_key * key)31*4882a593Smuzhiyun static void mmio_event_get_key(struct evsel *evsel, struct perf_sample *sample,
32*4882a593Smuzhiyun 			       struct event_key *key)
33*4882a593Smuzhiyun {
34*4882a593Smuzhiyun 	key->key  = evsel__intval(evsel, sample, "gpa");
35*4882a593Smuzhiyun 	key->info = evsel__intval(evsel, sample, "type");
36*4882a593Smuzhiyun }
37*4882a593Smuzhiyun 
38*4882a593Smuzhiyun #define KVM_TRACE_MMIO_READ_UNSATISFIED 0
39*4882a593Smuzhiyun #define KVM_TRACE_MMIO_READ 1
40*4882a593Smuzhiyun #define KVM_TRACE_MMIO_WRITE 2
41*4882a593Smuzhiyun 
mmio_event_begin(struct evsel * evsel,struct perf_sample * sample,struct event_key * key)42*4882a593Smuzhiyun static bool mmio_event_begin(struct evsel *evsel,
43*4882a593Smuzhiyun 			     struct perf_sample *sample, struct event_key *key)
44*4882a593Smuzhiyun {
45*4882a593Smuzhiyun 	/* MMIO read begin event in kernel. */
46*4882a593Smuzhiyun 	if (kvm_exit_event(evsel))
47*4882a593Smuzhiyun 		return true;
48*4882a593Smuzhiyun 
49*4882a593Smuzhiyun 	/* MMIO write begin event in kernel. */
50*4882a593Smuzhiyun 	if (!strcmp(evsel->name, "kvm:kvm_mmio") &&
51*4882a593Smuzhiyun 	    evsel__intval(evsel, sample, "type") == KVM_TRACE_MMIO_WRITE) {
52*4882a593Smuzhiyun 		mmio_event_get_key(evsel, sample, key);
53*4882a593Smuzhiyun 		return true;
54*4882a593Smuzhiyun 	}
55*4882a593Smuzhiyun 
56*4882a593Smuzhiyun 	return false;
57*4882a593Smuzhiyun }
58*4882a593Smuzhiyun 
mmio_event_end(struct evsel * evsel,struct perf_sample * sample,struct event_key * key)59*4882a593Smuzhiyun static bool mmio_event_end(struct evsel *evsel, struct perf_sample *sample,
60*4882a593Smuzhiyun 			   struct event_key *key)
61*4882a593Smuzhiyun {
62*4882a593Smuzhiyun 	/* MMIO write end event in kernel. */
63*4882a593Smuzhiyun 	if (kvm_entry_event(evsel))
64*4882a593Smuzhiyun 		return true;
65*4882a593Smuzhiyun 
66*4882a593Smuzhiyun 	/* MMIO read end event in kernel.*/
67*4882a593Smuzhiyun 	if (!strcmp(evsel->name, "kvm:kvm_mmio") &&
68*4882a593Smuzhiyun 	    evsel__intval(evsel, sample, "type") == KVM_TRACE_MMIO_READ) {
69*4882a593Smuzhiyun 		mmio_event_get_key(evsel, sample, key);
70*4882a593Smuzhiyun 		return true;
71*4882a593Smuzhiyun 	}
72*4882a593Smuzhiyun 
73*4882a593Smuzhiyun 	return false;
74*4882a593Smuzhiyun }
75*4882a593Smuzhiyun 
mmio_event_decode_key(struct perf_kvm_stat * kvm __maybe_unused,struct event_key * key,char * decode)76*4882a593Smuzhiyun static void mmio_event_decode_key(struct perf_kvm_stat *kvm __maybe_unused,
77*4882a593Smuzhiyun 				  struct event_key *key,
78*4882a593Smuzhiyun 				  char *decode)
79*4882a593Smuzhiyun {
80*4882a593Smuzhiyun 	scnprintf(decode, decode_str_len, "%#lx:%s",
81*4882a593Smuzhiyun 		  (unsigned long)key->key,
82*4882a593Smuzhiyun 		  key->info == KVM_TRACE_MMIO_WRITE ? "W" : "R");
83*4882a593Smuzhiyun }
84*4882a593Smuzhiyun 
85*4882a593Smuzhiyun static struct kvm_events_ops mmio_events = {
86*4882a593Smuzhiyun 	.is_begin_event = mmio_event_begin,
87*4882a593Smuzhiyun 	.is_end_event = mmio_event_end,
88*4882a593Smuzhiyun 	.decode_key = mmio_event_decode_key,
89*4882a593Smuzhiyun 	.name = "MMIO Access"
90*4882a593Smuzhiyun };
91*4882a593Smuzhiyun 
92*4882a593Smuzhiyun  /* The time of emulation pio access is from kvm_pio to kvm_entry. */
ioport_event_get_key(struct evsel * evsel,struct perf_sample * sample,struct event_key * key)93*4882a593Smuzhiyun static void ioport_event_get_key(struct evsel *evsel,
94*4882a593Smuzhiyun 				 struct perf_sample *sample,
95*4882a593Smuzhiyun 				 struct event_key *key)
96*4882a593Smuzhiyun {
97*4882a593Smuzhiyun 	key->key  = evsel__intval(evsel, sample, "port");
98*4882a593Smuzhiyun 	key->info = evsel__intval(evsel, sample, "rw");
99*4882a593Smuzhiyun }
100*4882a593Smuzhiyun 
ioport_event_begin(struct evsel * evsel,struct perf_sample * sample,struct event_key * key)101*4882a593Smuzhiyun static bool ioport_event_begin(struct evsel *evsel,
102*4882a593Smuzhiyun 			       struct perf_sample *sample,
103*4882a593Smuzhiyun 			       struct event_key *key)
104*4882a593Smuzhiyun {
105*4882a593Smuzhiyun 	if (!strcmp(evsel->name, "kvm:kvm_pio")) {
106*4882a593Smuzhiyun 		ioport_event_get_key(evsel, sample, key);
107*4882a593Smuzhiyun 		return true;
108*4882a593Smuzhiyun 	}
109*4882a593Smuzhiyun 
110*4882a593Smuzhiyun 	return false;
111*4882a593Smuzhiyun }
112*4882a593Smuzhiyun 
ioport_event_end(struct evsel * evsel,struct perf_sample * sample __maybe_unused,struct event_key * key __maybe_unused)113*4882a593Smuzhiyun static bool ioport_event_end(struct evsel *evsel,
114*4882a593Smuzhiyun 			     struct perf_sample *sample __maybe_unused,
115*4882a593Smuzhiyun 			     struct event_key *key __maybe_unused)
116*4882a593Smuzhiyun {
117*4882a593Smuzhiyun 	return kvm_entry_event(evsel);
118*4882a593Smuzhiyun }
119*4882a593Smuzhiyun 
ioport_event_decode_key(struct perf_kvm_stat * kvm __maybe_unused,struct event_key * key,char * decode)120*4882a593Smuzhiyun static void ioport_event_decode_key(struct perf_kvm_stat *kvm __maybe_unused,
121*4882a593Smuzhiyun 				    struct event_key *key,
122*4882a593Smuzhiyun 				    char *decode)
123*4882a593Smuzhiyun {
124*4882a593Smuzhiyun 	scnprintf(decode, decode_str_len, "%#llx:%s",
125*4882a593Smuzhiyun 		  (unsigned long long)key->key,
126*4882a593Smuzhiyun 		  key->info ? "POUT" : "PIN");
127*4882a593Smuzhiyun }
128*4882a593Smuzhiyun 
129*4882a593Smuzhiyun static struct kvm_events_ops ioport_events = {
130*4882a593Smuzhiyun 	.is_begin_event = ioport_event_begin,
131*4882a593Smuzhiyun 	.is_end_event = ioport_event_end,
132*4882a593Smuzhiyun 	.decode_key = ioport_event_decode_key,
133*4882a593Smuzhiyun 	.name = "IO Port Access"
134*4882a593Smuzhiyun };
135*4882a593Smuzhiyun 
136*4882a593Smuzhiyun const char *kvm_events_tp[] = {
137*4882a593Smuzhiyun 	"kvm:kvm_entry",
138*4882a593Smuzhiyun 	"kvm:kvm_exit",
139*4882a593Smuzhiyun 	"kvm:kvm_mmio",
140*4882a593Smuzhiyun 	"kvm:kvm_pio",
141*4882a593Smuzhiyun 	NULL,
142*4882a593Smuzhiyun };
143*4882a593Smuzhiyun 
144*4882a593Smuzhiyun struct kvm_reg_events_ops kvm_reg_events_ops[] = {
145*4882a593Smuzhiyun 	{ .name = "vmexit", .ops = &exit_events },
146*4882a593Smuzhiyun 	{ .name = "mmio", .ops = &mmio_events },
147*4882a593Smuzhiyun 	{ .name = "ioport", .ops = &ioport_events },
148*4882a593Smuzhiyun 	{ NULL, NULL },
149*4882a593Smuzhiyun };
150*4882a593Smuzhiyun 
151*4882a593Smuzhiyun const char * const kvm_skip_events[] = {
152*4882a593Smuzhiyun 	"HLT",
153*4882a593Smuzhiyun 	NULL,
154*4882a593Smuzhiyun };
155*4882a593Smuzhiyun 
cpu_isa_init(struct perf_kvm_stat * kvm,const char * cpuid)156*4882a593Smuzhiyun int cpu_isa_init(struct perf_kvm_stat *kvm, const char *cpuid)
157*4882a593Smuzhiyun {
158*4882a593Smuzhiyun 	if (strstr(cpuid, "Intel")) {
159*4882a593Smuzhiyun 		kvm->exit_reasons = vmx_exit_reasons;
160*4882a593Smuzhiyun 		kvm->exit_reasons_isa = "VMX";
161*4882a593Smuzhiyun 	} else if (strstr(cpuid, "AMD") || strstr(cpuid, "Hygon")) {
162*4882a593Smuzhiyun 		kvm->exit_reasons = svm_exit_reasons;
163*4882a593Smuzhiyun 		kvm->exit_reasons_isa = "SVM";
164*4882a593Smuzhiyun 	} else
165*4882a593Smuzhiyun 		return -ENOTSUP;
166*4882a593Smuzhiyun 
167*4882a593Smuzhiyun 	return 0;
168*4882a593Smuzhiyun }
169