1 // SPDX-License-Identifier: GPL-2.0
2 #include <errno.h>
3 #include <regex.h>
4 #include <string.h>
5 #include <sys/auxv.h>
6 #include <linux/kernel.h>
7 #include <linux/zalloc.h>
8 
9 #include "perf_regs.h"
10 #include "../../../perf-sys.h"
11 #include "../../../util/debug.h"
12 #include "../../../util/event.h"
13 #include "../../../util/perf_regs.h"
14 
15 #ifndef HWCAP_SVE
16 #define HWCAP_SVE	(1 << 22)
17 #endif
18 
19 const struct sample_reg sample_reg_masks[] = {
20 	SMPL_REG(x0, PERF_REG_ARM64_X0),
21 	SMPL_REG(x1, PERF_REG_ARM64_X1),
22 	SMPL_REG(x2, PERF_REG_ARM64_X2),
23 	SMPL_REG(x3, PERF_REG_ARM64_X3),
24 	SMPL_REG(x4, PERF_REG_ARM64_X4),
25 	SMPL_REG(x5, PERF_REG_ARM64_X5),
26 	SMPL_REG(x6, PERF_REG_ARM64_X6),
27 	SMPL_REG(x7, PERF_REG_ARM64_X7),
28 	SMPL_REG(x8, PERF_REG_ARM64_X8),
29 	SMPL_REG(x9, PERF_REG_ARM64_X9),
30 	SMPL_REG(x10, PERF_REG_ARM64_X10),
31 	SMPL_REG(x11, PERF_REG_ARM64_X11),
32 	SMPL_REG(x12, PERF_REG_ARM64_X12),
33 	SMPL_REG(x13, PERF_REG_ARM64_X13),
34 	SMPL_REG(x14, PERF_REG_ARM64_X14),
35 	SMPL_REG(x15, PERF_REG_ARM64_X15),
36 	SMPL_REG(x16, PERF_REG_ARM64_X16),
37 	SMPL_REG(x17, PERF_REG_ARM64_X17),
38 	SMPL_REG(x18, PERF_REG_ARM64_X18),
39 	SMPL_REG(x19, PERF_REG_ARM64_X19),
40 	SMPL_REG(x20, PERF_REG_ARM64_X20),
41 	SMPL_REG(x21, PERF_REG_ARM64_X21),
42 	SMPL_REG(x22, PERF_REG_ARM64_X22),
43 	SMPL_REG(x23, PERF_REG_ARM64_X23),
44 	SMPL_REG(x24, PERF_REG_ARM64_X24),
45 	SMPL_REG(x25, PERF_REG_ARM64_X25),
46 	SMPL_REG(x26, PERF_REG_ARM64_X26),
47 	SMPL_REG(x27, PERF_REG_ARM64_X27),
48 	SMPL_REG(x28, PERF_REG_ARM64_X28),
49 	SMPL_REG(x29, PERF_REG_ARM64_X29),
50 	SMPL_REG(lr, PERF_REG_ARM64_LR),
51 	SMPL_REG(sp, PERF_REG_ARM64_SP),
52 	SMPL_REG(pc, PERF_REG_ARM64_PC),
53 	SMPL_REG(vg, PERF_REG_ARM64_VG),
54 	SMPL_REG_END
55 };
56 
57 /* %xNUM */
58 #define SDT_OP_REGEX1  "^(x[1-2]?[0-9]|3[0-1])$"
59 
60 /* [sp], [sp, NUM] */
61 #define SDT_OP_REGEX2  "^\\[sp(, )?([0-9]+)?\\]$"
62 
63 static regex_t sdt_op_regex1, sdt_op_regex2;
64 
65 static int sdt_init_op_regex(void)
66 {
67 	static int initialized;
68 	int ret = 0;
69 
70 	if (initialized)
71 		return 0;
72 
73 	ret = regcomp(&sdt_op_regex1, SDT_OP_REGEX1, REG_EXTENDED);
74 	if (ret)
75 		goto error;
76 
77 	ret = regcomp(&sdt_op_regex2, SDT_OP_REGEX2, REG_EXTENDED);
78 	if (ret)
79 		goto free_regex1;
80 
81 	initialized = 1;
82 	return 0;
83 
84 free_regex1:
85 	regfree(&sdt_op_regex1);
86 error:
87 	pr_debug4("Regex compilation error.\n");
88 	return ret;
89 }
90 
91 /*
92  * SDT marker arguments on Arm64 uses %xREG or [sp, NUM], currently
93  * support these two formats.
94  */
95 int arch_sdt_arg_parse_op(char *old_op, char **new_op)
96 {
97 	int ret, new_len;
98 	regmatch_t rm[5];
99 
100 	ret = sdt_init_op_regex();
101 	if (ret < 0)
102 		return ret;
103 
104 	if (!regexec(&sdt_op_regex1, old_op, 3, rm, 0)) {
105 		/* Extract xNUM */
106 		new_len = 2;	/* % NULL */
107 		new_len += (int)(rm[1].rm_eo - rm[1].rm_so);
108 
109 		*new_op = zalloc(new_len);
110 		if (!*new_op)
111 			return -ENOMEM;
112 
113 		scnprintf(*new_op, new_len, "%%%.*s",
114 			(int)(rm[1].rm_eo - rm[1].rm_so), old_op + rm[1].rm_so);
115 	} else if (!regexec(&sdt_op_regex2, old_op, 5, rm, 0)) {
116 		/* [sp], [sp, NUM] or [sp,NUM] */
117 		new_len = 7;	/* + ( % s p ) NULL */
118 
119 		/* If the argument is [sp], need to fill offset '0' */
120 		if (rm[2].rm_so == -1)
121 			new_len += 1;
122 		else
123 			new_len += (int)(rm[2].rm_eo - rm[2].rm_so);
124 
125 		*new_op = zalloc(new_len);
126 		if (!*new_op)
127 			return -ENOMEM;
128 
129 		if (rm[2].rm_so == -1)
130 			scnprintf(*new_op, new_len, "+0(%%sp)");
131 		else
132 			scnprintf(*new_op, new_len, "+%.*s(%%sp)",
133 				  (int)(rm[2].rm_eo - rm[2].rm_so),
134 				  old_op + rm[2].rm_so);
135 	} else {
136 		pr_debug4("Skipping unsupported SDT argument: %s\n", old_op);
137 		return SDT_ARG_SKIP;
138 	}
139 
140 	return SDT_ARG_VALID;
141 }
142 
143 uint64_t arch__intr_reg_mask(void)
144 {
145 	return PERF_REGS_MASK;
146 }
147 
148 uint64_t arch__user_reg_mask(void)
149 {
150 	struct perf_event_attr attr = {
151 		.type                   = PERF_TYPE_HARDWARE,
152 		.config                 = PERF_COUNT_HW_CPU_CYCLES,
153 		.sample_type            = PERF_SAMPLE_REGS_USER,
154 		.disabled               = 1,
155 		.exclude_kernel         = 1,
156 		.sample_period		= 1,
157 		.sample_regs_user	= PERF_REGS_MASK
158 	};
159 	int fd;
160 
161 	if (getauxval(AT_HWCAP) & HWCAP_SVE)
162 		attr.sample_regs_user |= SMPL_REG_MASK(PERF_REG_ARM64_VG);
163 
164 	/*
165 	 * Check if the pmu supports perf extended regs, before
166 	 * returning the register mask to sample.
167 	 */
168 	if (attr.sample_regs_user != PERF_REGS_MASK) {
169 		event_attr_init(&attr);
170 		fd = sys_perf_event_open(&attr, 0, -1, -1, 0);
171 		if (fd != -1) {
172 			close(fd);
173 			return attr.sample_regs_user;
174 		}
175 	}
176 	return PERF_REGS_MASK;
177 }
178