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