summaryrefslogtreecommitdiffstats
path: root/tools/perf/util/kvm-stat-arch/kvm-stat-loongarch.c
blob: a04cd09e3361290e0f201db0a9a9ca648cf4d37d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
// SPDX-License-Identifier: GPL-2.0
#include <errno.h>
#include <memory.h>
#include <dwarf-regs.h>
#include "../kvm-stat.h"
#include "../parse-events.h"
#include "../debug.h"
#include "../evsel.h"
#include "../evlist.h"
#include "../pmus.h"

#define LOONGARCH_EXCEPTION_INT		0
#define LOONGARCH_EXCEPTION_PIL		1
#define LOONGARCH_EXCEPTION_PIS		2
#define LOONGARCH_EXCEPTION_PIF		3
#define LOONGARCH_EXCEPTION_PME		4
#define LOONGARCH_EXCEPTION_FPD		15
#define LOONGARCH_EXCEPTION_SXD		16
#define LOONGARCH_EXCEPTION_ASXD	17
#define LOONGARCH_EXCEPTION_GSPR	22
#define  LOONGARCH_EXCEPTION_CPUCFG	100
#define  LOONGARCH_EXCEPTION_CSR	101
#define  LOONGARCH_EXCEPTION_IOCSR	102
#define  LOONGARCH_EXCEPTION_IDLE	103
#define  LOONGARCH_EXCEPTION_OTHERS	104
#define LOONGARCH_EXCEPTION_HVC		23

#define loongarch_exception_type				\
	{LOONGARCH_EXCEPTION_INT,  "Interrupt" },		\
	{LOONGARCH_EXCEPTION_PIL,  "Mem Read" },		\
	{LOONGARCH_EXCEPTION_PIS,  "Mem Store" },		\
	{LOONGARCH_EXCEPTION_PIF,  "Inst Fetch" },		\
	{LOONGARCH_EXCEPTION_PME,  "Mem Modify" },		\
	{LOONGARCH_EXCEPTION_FPD,  "FPU" },			\
	{LOONGARCH_EXCEPTION_SXD,  "LSX" },			\
	{LOONGARCH_EXCEPTION_ASXD, "LASX" },			\
	{LOONGARCH_EXCEPTION_GSPR, "Privilege Error" },		\
	{LOONGARCH_EXCEPTION_HVC,  "Hypercall" },		\
	{LOONGARCH_EXCEPTION_CPUCFG, "CPUCFG" },		\
	{LOONGARCH_EXCEPTION_CSR,    "CSR" },			\
	{LOONGARCH_EXCEPTION_IOCSR,  "IOCSR" },			\
	{LOONGARCH_EXCEPTION_IDLE,   "Idle" },			\
	{LOONGARCH_EXCEPTION_OTHERS, "Others" }

define_exit_reasons_table(loongarch_exit_reasons, loongarch_exception_type);

static const char *kvm_reenter_trace = "kvm:kvm_reenter";
static const char * const __kvm_events_tp[] = {
	"kvm:kvm_enter",
	"kvm:kvm_reenter",
	"kvm:kvm_exit",
	"kvm:kvm_exit_gspr",
	NULL,
};

static bool event_begin(struct perf_sample *sample, struct event_key *key)
{
	return exit_event_begin(sample, key);
}

static bool event_end(struct perf_sample *sample,
		      struct event_key *key __maybe_unused)
{
	/*
	 * LoongArch kvm is different with other architectures
	 *
	 * There is kvm:kvm_reenter or kvm:kvm_enter event adjacent with
	 * kvm:kvm_exit event.
	 *   kvm:kvm_enter   means returning to vmm and then to guest
	 *   kvm:kvm_reenter means returning to guest immediately
	 */
	return evsel__name_is(sample->evsel, kvm_entry_trace(EM_LOONGARCH)) ||
	       evsel__name_is(sample->evsel, kvm_reenter_trace);
}

static void event_gspr_get_key(struct perf_sample *sample, struct event_key *key)
{
	unsigned int insn;

	key->key = LOONGARCH_EXCEPTION_OTHERS;
	insn = perf_sample__intval(sample, "inst_word");

	switch (insn >> 24) {
	case 0:
		/* CPUCFG inst trap */
		if ((insn >> 10) == 0x1b)
			key->key = LOONGARCH_EXCEPTION_CPUCFG;
		break;
	case 4:
		/* CSR inst trap */
		key->key = LOONGARCH_EXCEPTION_CSR;
		break;
	case 6:
		/* IOCSR inst trap */
		if ((insn >> 15) == 0xc90)
			key->key = LOONGARCH_EXCEPTION_IOCSR;
		else if ((insn >> 15) == 0xc91)
			/* Idle inst trap */
			key->key = LOONGARCH_EXCEPTION_IDLE;
		break;
	default:
		key->key = LOONGARCH_EXCEPTION_OTHERS;
		break;
	}
}

static const struct child_event_ops child_events[] = {
	{ .name = "kvm:kvm_exit_gspr", .get_key = event_gspr_get_key },
	{ NULL, NULL },
};

static const struct kvm_events_ops exit_events = {
	.is_begin_event = event_begin,
	.is_end_event = event_end,
	.child_ops = child_events,
	.decode_key = exit_event_decode_key,
	.name = "VM-EXIT"
};

static const struct kvm_reg_events_ops __kvm_reg_events_ops[] = {
	{ .name	= "vmexit", .ops = &exit_events, },
	{ NULL, NULL },
};

static const char * const __kvm_skip_events[] = {
	NULL,
};

int __cpu_isa_init_loongarch(struct perf_kvm_stat *kvm)
{
	kvm->exit_reasons_isa = "loongarch64";
	kvm->exit_reasons = loongarch_exit_reasons;
	return 0;
}

const char * const *__kvm_events_tp_loongarch(void)
{
	return __kvm_events_tp;
}

const struct kvm_reg_events_ops *__kvm_reg_events_ops_loongarch(void)
{
	return __kvm_reg_events_ops;
}

const char * const *__kvm_skip_events_loongarch(void)
{
	return __kvm_skip_events;
}