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 | static 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(buf: *new_op, size: new_len, fmt: "+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: &attr); |
170 | fd = sys_perf_event_open(attr: &attr, pid: 0, cpu: -1, group_fd: -1, flags: 0); |
171 | if (fd != -1) { |
172 | close(fd); |
173 | return attr.sample_regs_user; |
174 | } |
175 | } |
176 | return PERF_REGS_MASK; |
177 | } |
178 | |
179 | const struct sample_reg *arch__sample_reg_masks(void) |
180 | { |
181 | return sample_reg_masks; |
182 | } |
183 | |