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