xref: /linux/tools/perf/arch/loongarch/util/kvm-stat.c (revision a1ff5a7d78a036d6c2178ee5acd6ba4946243800)
1 // SPDX-License-Identifier: GPL-2.0
2 #include <errno.h>
3 #include <memory.h>
4 #include "util/kvm-stat.h"
5 #include "util/parse-events.h"
6 #include "util/debug.h"
7 #include "util/evsel.h"
8 #include "util/evlist.h"
9 #include "util/pmus.h"
10 
11 #define LOONGARCH_EXCEPTION_INT		0
12 #define LOONGARCH_EXCEPTION_PIL		1
13 #define LOONGARCH_EXCEPTION_PIS		2
14 #define LOONGARCH_EXCEPTION_PIF		3
15 #define LOONGARCH_EXCEPTION_PME		4
16 #define LOONGARCH_EXCEPTION_FPD		15
17 #define LOONGARCH_EXCEPTION_SXD		16
18 #define LOONGARCH_EXCEPTION_ASXD	17
19 #define LOONGARCH_EXCEPTION_GSPR	22
20 #define  LOONGARCH_EXCEPTION_CPUCFG	100
21 #define  LOONGARCH_EXCEPTION_CSR	101
22 #define  LOONGARCH_EXCEPTION_IOCSR	102
23 #define  LOONGARCH_EXCEPTION_IDLE	103
24 #define  LOONGARCH_EXCEPTION_OTHERS	104
25 #define LOONGARCH_EXCEPTION_HVC		23
26 
27 #define loongarch_exception_type				\
28 	{LOONGARCH_EXCEPTION_INT,  "Interrupt" },		\
29 	{LOONGARCH_EXCEPTION_PIL,  "Mem Read" },		\
30 	{LOONGARCH_EXCEPTION_PIS,  "Mem Store" },		\
31 	{LOONGARCH_EXCEPTION_PIF,  "Inst Fetch" },		\
32 	{LOONGARCH_EXCEPTION_PME,  "Mem Modify" },		\
33 	{LOONGARCH_EXCEPTION_FPD,  "FPU" },			\
34 	{LOONGARCH_EXCEPTION_SXD,  "LSX" },			\
35 	{LOONGARCH_EXCEPTION_ASXD, "LASX" },			\
36 	{LOONGARCH_EXCEPTION_GSPR, "Privilege Error" },		\
37 	{LOONGARCH_EXCEPTION_HVC,  "Hypercall" },		\
38 	{LOONGARCH_EXCEPTION_CPUCFG, "CPUCFG" },		\
39 	{LOONGARCH_EXCEPTION_CSR,    "CSR" },			\
40 	{LOONGARCH_EXCEPTION_IOCSR,  "IOCSR" },			\
41 	{LOONGARCH_EXCEPTION_IDLE,   "Idle" },			\
42 	{LOONGARCH_EXCEPTION_OTHERS, "Others" }
43 
44 define_exit_reasons_table(loongarch_exit_reasons, loongarch_exception_type);
45 
46 const char *vcpu_id_str = "vcpu_id";
47 const char *kvm_exit_reason = "reason";
48 const char *kvm_entry_trace = "kvm:kvm_enter";
49 const char *kvm_reenter_trace = "kvm:kvm_reenter";
50 const char *kvm_exit_trace = "kvm:kvm_exit";
51 const char *kvm_events_tp[] = {
52 	"kvm:kvm_enter",
53 	"kvm:kvm_reenter",
54 	"kvm:kvm_exit",
55 	"kvm:kvm_exit_gspr",
56 	NULL,
57 };
58 
event_begin(struct evsel * evsel,struct perf_sample * sample,struct event_key * key)59 static bool event_begin(struct evsel *evsel,
60 			struct perf_sample *sample, struct event_key *key)
61 {
62 	return exit_event_begin(evsel, sample, key);
63 }
64 
event_end(struct evsel * evsel,struct perf_sample * sample __maybe_unused,struct event_key * key __maybe_unused)65 static bool event_end(struct evsel *evsel,
66 		      struct perf_sample *sample __maybe_unused,
67 		      struct event_key *key __maybe_unused)
68 {
69 	/*
70 	 * LoongArch kvm is different with other architectures
71 	 *
72 	 * There is kvm:kvm_reenter or kvm:kvm_enter event adjacent with
73 	 * kvm:kvm_exit event.
74 	 *   kvm:kvm_enter   means returning to vmm and then to guest
75 	 *   kvm:kvm_reenter means returning to guest immediately
76 	 */
77 	return evsel__name_is(evsel, kvm_entry_trace) || evsel__name_is(evsel, kvm_reenter_trace);
78 }
79 
event_gspr_get_key(struct evsel * evsel,struct perf_sample * sample,struct event_key * key)80 static void event_gspr_get_key(struct evsel *evsel,
81 			       struct perf_sample *sample, struct event_key *key)
82 {
83 	unsigned int insn;
84 
85 	key->key = LOONGARCH_EXCEPTION_OTHERS;
86 	insn = evsel__intval(evsel, sample, "inst_word");
87 
88 	switch (insn >> 24) {
89 	case 0:
90 		/* CPUCFG inst trap */
91 		if ((insn >> 10) == 0x1b)
92 			key->key = LOONGARCH_EXCEPTION_CPUCFG;
93 		break;
94 	case 4:
95 		/* CSR inst trap */
96 		key->key = LOONGARCH_EXCEPTION_CSR;
97 		break;
98 	case 6:
99 		/* IOCSR inst trap */
100 		if ((insn >> 15) == 0xc90)
101 			key->key = LOONGARCH_EXCEPTION_IOCSR;
102 		else if ((insn >> 15) == 0xc91)
103 			/* Idle inst trap */
104 			key->key = LOONGARCH_EXCEPTION_IDLE;
105 		break;
106 	default:
107 		key->key = LOONGARCH_EXCEPTION_OTHERS;
108 		break;
109 	}
110 }
111 
112 static struct child_event_ops child_events[] = {
113 	{ .name = "kvm:kvm_exit_gspr", .get_key = event_gspr_get_key },
114 	{ NULL, NULL },
115 };
116 
117 static struct kvm_events_ops exit_events = {
118 	.is_begin_event = event_begin,
119 	.is_end_event = event_end,
120 	.child_ops = child_events,
121 	.decode_key = exit_event_decode_key,
122 	.name = "VM-EXIT"
123 };
124 
125 struct kvm_reg_events_ops kvm_reg_events_ops[] = {
126 	{ .name	= "vmexit", .ops = &exit_events, },
127 	{ NULL, NULL },
128 };
129 
130 const char * const kvm_skip_events[] = {
131 	NULL,
132 };
133 
cpu_isa_init(struct perf_kvm_stat * kvm,const char * cpuid __maybe_unused)134 int cpu_isa_init(struct perf_kvm_stat *kvm, const char *cpuid __maybe_unused)
135 {
136 	kvm->exit_reasons_isa = "loongarch64";
137 	kvm->exit_reasons = loongarch_exit_reasons;
138 	return 0;
139 }
140