1 // SPDX-License-Identifier: GPL-2.0
2 #include <errno.h>
3 #include <string.h>
4 #include "../kvm-stat.h"
5 #include "../evsel.h"
6 #include "../env.h"
7 #include "../../arch/x86/include/uapi/asm/svm.h"
8 #include "../../arch/x86/include/uapi/asm/vmx.h"
9 #include "../../arch/x86/include/uapi/asm/kvm.h"
10 #include <subcmd/parse-options.h>
11
12 define_exit_reasons_table(vmx_exit_reasons, VMX_EXIT_REASONS);
13 define_exit_reasons_table(svm_exit_reasons, SVM_EXIT_REASONS);
14
15 static const struct kvm_events_ops exit_events = {
16 .is_begin_event = exit_event_begin,
17 .is_end_event = exit_event_end,
18 .decode_key = exit_event_decode_key,
19 .name = "VM-EXIT"
20 };
21
22 /*
23 * For the mmio events, we treat:
24 * the time of MMIO write: kvm_mmio(KVM_TRACE_MMIO_WRITE...) -> kvm_entry
25 * the time of MMIO read: kvm_exit -> kvm_mmio(KVM_TRACE_MMIO_READ...).
26 */
mmio_event_get_key(struct evsel * evsel,struct perf_sample * sample,struct event_key * key)27 static void mmio_event_get_key(struct evsel *evsel, struct perf_sample *sample,
28 struct event_key *key)
29 {
30 key->key = evsel__intval(evsel, sample, "gpa");
31 key->info = evsel__intval(evsel, sample, "type");
32 }
33
34 #define KVM_TRACE_MMIO_READ_UNSATISFIED 0
35 #define KVM_TRACE_MMIO_READ 1
36 #define KVM_TRACE_MMIO_WRITE 2
37
mmio_event_begin(struct evsel * evsel,struct perf_sample * sample,struct event_key * key)38 static bool mmio_event_begin(struct evsel *evsel,
39 struct perf_sample *sample, struct event_key *key)
40 {
41 /* MMIO read begin event in kernel. */
42 if (kvm_exit_event(evsel))
43 return true;
44
45 /* MMIO write begin event in kernel. */
46 if (evsel__name_is(evsel, "kvm:kvm_mmio") &&
47 evsel__intval(evsel, sample, "type") == KVM_TRACE_MMIO_WRITE) {
48 mmio_event_get_key(evsel, sample, key);
49 return true;
50 }
51
52 return false;
53 }
54
mmio_event_end(struct evsel * evsel,struct perf_sample * sample,struct event_key * key)55 static bool mmio_event_end(struct evsel *evsel, struct perf_sample *sample,
56 struct event_key *key)
57 {
58 /* MMIO write end event in kernel. */
59 if (kvm_entry_event(evsel))
60 return true;
61
62 /* MMIO read end event in kernel.*/
63 if (evsel__name_is(evsel, "kvm:kvm_mmio") &&
64 evsel__intval(evsel, sample, "type") == KVM_TRACE_MMIO_READ) {
65 mmio_event_get_key(evsel, sample, key);
66 return true;
67 }
68
69 return false;
70 }
71
mmio_event_decode_key(struct perf_kvm_stat * kvm __maybe_unused,struct event_key * key,char * decode)72 static void mmio_event_decode_key(struct perf_kvm_stat *kvm __maybe_unused,
73 struct event_key *key,
74 char *decode)
75 {
76 scnprintf(decode, KVM_EVENT_NAME_LEN, "%#lx:%s",
77 (unsigned long)key->key,
78 key->info == KVM_TRACE_MMIO_WRITE ? "W" : "R");
79 }
80
81 static const struct kvm_events_ops mmio_events = {
82 .is_begin_event = mmio_event_begin,
83 .is_end_event = mmio_event_end,
84 .decode_key = mmio_event_decode_key,
85 .name = "MMIO Access"
86 };
87
88 /* 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)89 static void ioport_event_get_key(struct evsel *evsel,
90 struct perf_sample *sample,
91 struct event_key *key)
92 {
93 key->key = evsel__intval(evsel, sample, "port");
94 key->info = evsel__intval(evsel, sample, "rw");
95 }
96
ioport_event_begin(struct evsel * evsel,struct perf_sample * sample,struct event_key * key)97 static bool ioport_event_begin(struct evsel *evsel,
98 struct perf_sample *sample,
99 struct event_key *key)
100 {
101 if (evsel__name_is(evsel, "kvm:kvm_pio")) {
102 ioport_event_get_key(evsel, sample, key);
103 return true;
104 }
105
106 return false;
107 }
108
ioport_event_end(struct evsel * evsel,struct perf_sample * sample __maybe_unused,struct event_key * key __maybe_unused)109 static bool ioport_event_end(struct evsel *evsel,
110 struct perf_sample *sample __maybe_unused,
111 struct event_key *key __maybe_unused)
112 {
113 return kvm_entry_event(evsel);
114 }
115
ioport_event_decode_key(struct perf_kvm_stat * kvm __maybe_unused,struct event_key * key,char * decode)116 static void ioport_event_decode_key(struct perf_kvm_stat *kvm __maybe_unused,
117 struct event_key *key,
118 char *decode)
119 {
120 scnprintf(decode, KVM_EVENT_NAME_LEN, "%#llx:%s",
121 (unsigned long long)key->key,
122 key->info ? "POUT" : "PIN");
123 }
124
125 static const struct kvm_events_ops ioport_events = {
126 .is_begin_event = ioport_event_begin,
127 .is_end_event = ioport_event_end,
128 .decode_key = ioport_event_decode_key,
129 .name = "IO Port Access"
130 };
131
132 /* The time of emulation msr is from kvm_msr to kvm_entry. */
msr_event_get_key(struct evsel * evsel,struct perf_sample * sample,struct event_key * key)133 static void msr_event_get_key(struct evsel *evsel,
134 struct perf_sample *sample,
135 struct event_key *key)
136 {
137 key->key = evsel__intval(evsel, sample, "ecx");
138 key->info = evsel__intval(evsel, sample, "write");
139 }
140
msr_event_begin(struct evsel * evsel,struct perf_sample * sample,struct event_key * key)141 static bool msr_event_begin(struct evsel *evsel,
142 struct perf_sample *sample,
143 struct event_key *key)
144 {
145 if (evsel__name_is(evsel, "kvm:kvm_msr")) {
146 msr_event_get_key(evsel, sample, key);
147 return true;
148 }
149
150 return false;
151 }
152
msr_event_end(struct evsel * evsel,struct perf_sample * sample __maybe_unused,struct event_key * key __maybe_unused)153 static bool msr_event_end(struct evsel *evsel,
154 struct perf_sample *sample __maybe_unused,
155 struct event_key *key __maybe_unused)
156 {
157 return kvm_entry_event(evsel);
158 }
159
msr_event_decode_key(struct perf_kvm_stat * kvm __maybe_unused,struct event_key * key,char * decode)160 static void msr_event_decode_key(struct perf_kvm_stat *kvm __maybe_unused,
161 struct event_key *key,
162 char *decode)
163 {
164 scnprintf(decode, KVM_EVENT_NAME_LEN, "%#llx:%s",
165 (unsigned long long)key->key,
166 key->info ? "W" : "R");
167 }
168
169 static const struct kvm_events_ops msr_events = {
170 .is_begin_event = msr_event_begin,
171 .is_end_event = msr_event_end,
172 .decode_key = msr_event_decode_key,
173 .name = "MSR Access"
174 };
175
176 static const char * const __kvm_events_tp[] = {
177 "kvm:kvm_entry",
178 "kvm:kvm_exit",
179 "kvm:kvm_mmio",
180 "kvm:kvm_pio",
181 "kvm:kvm_msr",
182 NULL,
183 };
184
185 static const struct kvm_reg_events_ops __kvm_reg_events_ops[] = {
186 { .name = "vmexit", .ops = &exit_events },
187 { .name = "mmio", .ops = &mmio_events },
188 { .name = "ioport", .ops = &ioport_events },
189 { .name = "msr", .ops = &msr_events },
190 { NULL, NULL },
191 };
192
193 static const char * const __kvm_skip_events[] = {
194 "HLT",
195 NULL,
196 };
197
__cpu_isa_init_x86(struct perf_kvm_stat * kvm,const char * cpuid)198 int __cpu_isa_init_x86(struct perf_kvm_stat *kvm, const char *cpuid)
199 {
200 if (strstr(cpuid, "Intel")) {
201 kvm->exit_reasons = vmx_exit_reasons;
202 kvm->exit_reasons_isa = "VMX";
203 } else if (strstr(cpuid, "AMD") || strstr(cpuid, "Hygon")) {
204 kvm->exit_reasons = svm_exit_reasons;
205 kvm->exit_reasons_isa = "SVM";
206 } else
207 return -ENOTSUP;
208
209 return 0;
210 }
211
212 /*
213 * After KVM supports PEBS for guest on Intel platforms
214 * (https://lore.kernel.org/all/20220411101946.20262-1-likexu@tencent.com/),
215 * host loses the capability to sample guest with PEBS since all PEBS related
216 * MSRs are switched to guest value after vm-entry, like IA32_DS_AREA MSR is
217 * switched to guest GVA at vm-entry. This would lead to "perf kvm record"
218 * fails to sample guest on Intel platforms since "cycles:P" event is used to
219 * sample guest by default.
220 *
221 * So, to avoid this issue explicitly use "cycles" instead of "cycles:P" event
222 * by default to sample guest on Intel platforms.
223 */
__kvm_add_default_arch_event_x86(int * argc,const char ** argv)224 int __kvm_add_default_arch_event_x86(int *argc, const char **argv)
225 {
226 const char **tmp;
227 bool event = false;
228 int ret = 0, i, j = *argc;
229
230 const struct option event_options[] = {
231 OPT_BOOLEAN('e', "event", &event, NULL),
232 OPT_BOOLEAN(0, "pfm-events", &event, NULL),
233 OPT_END()
234 };
235
236 if (!x86__is_intel_cpu())
237 return 0;
238
239 tmp = calloc(j + 1, sizeof(char *));
240 if (!tmp)
241 return -ENOMEM;
242
243 for (i = 0; i < j; i++)
244 tmp[i] = argv[i];
245
246 parse_options(j, tmp, event_options, NULL, PARSE_OPT_KEEP_UNKNOWN);
247 if (!event) {
248 argv[j++] = STRDUP_FAIL_EXIT("-e");
249 argv[j++] = STRDUP_FAIL_EXIT("cycles");
250 *argc += 2;
251 }
252
253 free(tmp);
254 return 0;
255
256 EXIT:
257 free(tmp);
258 return ret;
259 }
260
__kvm_events_tp_x86(void)261 const char * const *__kvm_events_tp_x86(void)
262 {
263 return __kvm_events_tp;
264 }
265
__kvm_reg_events_ops_x86(void)266 const struct kvm_reg_events_ops *__kvm_reg_events_ops_x86(void)
267 {
268 return __kvm_reg_events_ops;
269 }
270
__kvm_skip_events_x86(void)271 const char * const *__kvm_skip_events_x86(void)
272 {
273 return __kvm_skip_events;
274 }
275