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