]>
Commit | Line | Data |
---|---|---|
f50a7f3d | 1 | // SPDX-License-Identifier: GPL-2.0-only |
fcb45ec0 ME |
2 | /* |
3 | * Copyright 2013-2015, Michael Ellerman, IBM Corp. | |
fcb45ec0 ME |
4 | */ |
5 | ||
d1301afd ME |
6 | #define _GNU_SOURCE /* For CPU_ZERO etc. */ |
7 | ||
fcb45ec0 ME |
8 | #include <elf.h> |
9 | #include <errno.h> | |
10 | #include <fcntl.h> | |
11 | #include <link.h> | |
d1301afd | 12 | #include <sched.h> |
d2bf7932 | 13 | #include <signal.h> |
fcb45ec0 | 14 | #include <stdio.h> |
d2bf7932 | 15 | #include <stdlib.h> |
95f9b3af | 16 | #include <string.h> |
d2bf7932 | 17 | #include <sys/ioctl.h> |
fcb45ec0 ME |
18 | #include <sys/stat.h> |
19 | #include <sys/types.h> | |
95f9b3af | 20 | #include <sys/utsname.h> |
fcb45ec0 | 21 | #include <unistd.h> |
d2bf7932 NR |
22 | #include <asm/unistd.h> |
23 | #include <linux/limits.h> | |
fcb45ec0 ME |
24 | |
25 | #include "utils.h" | |
26 | ||
27 | static char auxv[4096]; | |
28 | ||
e3028437 | 29 | int read_auxv(char *buf, ssize_t buf_size) |
fcb45ec0 | 30 | { |
fcb45ec0 | 31 | ssize_t num; |
e3028437 | 32 | int rc, fd; |
fcb45ec0 ME |
33 | |
34 | fd = open("/proc/self/auxv", O_RDONLY); | |
35 | if (fd == -1) { | |
36 | perror("open"); | |
e3028437 | 37 | return -errno; |
fcb45ec0 ME |
38 | } |
39 | ||
e3028437 | 40 | num = read(fd, buf, buf_size); |
fcb45ec0 ME |
41 | if (num < 0) { |
42 | perror("read"); | |
e3028437 | 43 | rc = -EIO; |
fcb45ec0 ME |
44 | goto out; |
45 | } | |
46 | ||
e3028437 ME |
47 | if (num > buf_size) { |
48 | printf("overflowed auxv buffer\n"); | |
49 | rc = -EOVERFLOW; | |
fcb45ec0 ME |
50 | goto out; |
51 | } | |
52 | ||
e3028437 ME |
53 | rc = 0; |
54 | out: | |
55 | close(fd); | |
56 | return rc; | |
57 | } | |
58 | ||
59 | void *find_auxv_entry(int type, char *auxv) | |
60 | { | |
61 | ElfW(auxv_t) *p; | |
62 | ||
fcb45ec0 ME |
63 | p = (ElfW(auxv_t) *)auxv; |
64 | ||
65 | while (p->a_type != AT_NULL) { | |
e3028437 ME |
66 | if (p->a_type == type) |
67 | return p; | |
fcb45ec0 ME |
68 | |
69 | p++; | |
70 | } | |
e3028437 ME |
71 | |
72 | return NULL; | |
73 | } | |
74 | ||
75 | void *get_auxv_entry(int type) | |
76 | { | |
77 | ElfW(auxv_t) *p; | |
78 | ||
79 | if (read_auxv(auxv, sizeof(auxv))) | |
80 | return NULL; | |
81 | ||
82 | p = find_auxv_entry(type, auxv); | |
83 | if (p) | |
84 | return (void *)p->a_un.a_val; | |
85 | ||
86 | return NULL; | |
fcb45ec0 | 87 | } |
d1301afd ME |
88 | |
89 | int pick_online_cpu(void) | |
90 | { | |
91 | cpu_set_t mask; | |
92 | int cpu; | |
93 | ||
94 | CPU_ZERO(&mask); | |
95 | ||
96 | if (sched_getaffinity(0, sizeof(mask), &mask)) { | |
97 | perror("sched_getaffinity"); | |
98 | return -1; | |
99 | } | |
100 | ||
101 | /* We prefer a primary thread, but skip 0 */ | |
102 | for (cpu = 8; cpu < CPU_SETSIZE; cpu += 8) | |
103 | if (CPU_ISSET(cpu, &mask)) | |
104 | return cpu; | |
105 | ||
106 | /* Search for anything, but in reverse */ | |
107 | for (cpu = CPU_SETSIZE - 1; cpu >= 0; cpu--) | |
108 | if (CPU_ISSET(cpu, &mask)) | |
109 | return cpu; | |
110 | ||
111 | printf("No cpus in affinity mask?!\n"); | |
112 | return -1; | |
113 | } | |
95f9b3af ME |
114 | |
115 | bool is_ppc64le(void) | |
116 | { | |
117 | struct utsname uts; | |
118 | int rc; | |
119 | ||
120 | errno = 0; | |
121 | rc = uname(&uts); | |
122 | if (rc) { | |
123 | perror("uname"); | |
124 | return false; | |
125 | } | |
126 | ||
127 | return strcmp(uts.machine, "ppc64le") == 0; | |
128 | } | |
d2bf7932 NR |
129 | |
130 | int read_debugfs_file(char *debugfs_file, int *result) | |
131 | { | |
132 | int rc = -1, fd; | |
133 | char path[PATH_MAX]; | |
134 | char value[16]; | |
135 | ||
136 | strcpy(path, "/sys/kernel/debug/"); | |
137 | strncat(path, debugfs_file, PATH_MAX - strlen(path) - 1); | |
138 | ||
139 | if ((fd = open(path, O_RDONLY)) < 0) | |
140 | return rc; | |
141 | ||
142 | if ((rc = read(fd, value, sizeof(value))) < 0) | |
143 | return rc; | |
144 | ||
145 | value[15] = 0; | |
146 | *result = atoi(value); | |
147 | close(fd); | |
148 | ||
149 | return 0; | |
150 | } | |
151 | ||
152 | int write_debugfs_file(char *debugfs_file, int result) | |
153 | { | |
154 | int rc = -1, fd; | |
155 | char path[PATH_MAX]; | |
156 | char value[16]; | |
157 | ||
158 | strcpy(path, "/sys/kernel/debug/"); | |
159 | strncat(path, debugfs_file, PATH_MAX - strlen(path) - 1); | |
160 | ||
161 | if ((fd = open(path, O_WRONLY)) < 0) | |
162 | return rc; | |
163 | ||
164 | snprintf(value, 16, "%d", result); | |
165 | ||
166 | if ((rc = write(fd, value, strlen(value))) < 0) | |
167 | return rc; | |
168 | ||
169 | close(fd); | |
170 | ||
171 | return 0; | |
172 | } | |
173 | ||
174 | static long perf_event_open(struct perf_event_attr *hw_event, pid_t pid, | |
175 | int cpu, int group_fd, unsigned long flags) | |
176 | { | |
177 | return syscall(__NR_perf_event_open, hw_event, pid, cpu, | |
178 | group_fd, flags); | |
179 | } | |
180 | ||
181 | static void perf_event_attr_init(struct perf_event_attr *event_attr, | |
182 | unsigned int type, | |
183 | unsigned long config) | |
184 | { | |
185 | memset(event_attr, 0, sizeof(*event_attr)); | |
186 | ||
187 | event_attr->type = type; | |
188 | event_attr->size = sizeof(struct perf_event_attr); | |
189 | event_attr->config = config; | |
190 | event_attr->read_format = PERF_FORMAT_GROUP; | |
191 | event_attr->disabled = 1; | |
192 | event_attr->exclude_kernel = 1; | |
193 | event_attr->exclude_hv = 1; | |
194 | event_attr->exclude_guest = 1; | |
195 | } | |
196 | ||
197 | int perf_event_open_counter(unsigned int type, | |
198 | unsigned long config, int group_fd) | |
199 | { | |
200 | int fd; | |
201 | struct perf_event_attr event_attr; | |
202 | ||
203 | perf_event_attr_init(&event_attr, type, config); | |
204 | ||
205 | fd = perf_event_open(&event_attr, 0, -1, group_fd, 0); | |
206 | ||
207 | if (fd < 0) | |
208 | perror("perf_event_open() failed"); | |
209 | ||
210 | return fd; | |
211 | } | |
212 | ||
213 | int perf_event_enable(int fd) | |
214 | { | |
215 | if (ioctl(fd, PERF_EVENT_IOC_ENABLE, PERF_IOC_FLAG_GROUP) == -1) { | |
216 | perror("error while enabling perf events"); | |
217 | return -1; | |
218 | } | |
219 | ||
220 | return 0; | |
221 | } | |
222 | ||
223 | int perf_event_disable(int fd) | |
224 | { | |
225 | if (ioctl(fd, PERF_EVENT_IOC_DISABLE, PERF_IOC_FLAG_GROUP) == -1) { | |
226 | perror("error disabling perf events"); | |
227 | return -1; | |
228 | } | |
229 | ||
230 | return 0; | |
231 | } | |
232 | ||
233 | int perf_event_reset(int fd) | |
234 | { | |
235 | if (ioctl(fd, PERF_EVENT_IOC_RESET, PERF_IOC_FLAG_GROUP) == -1) { | |
236 | perror("error resetting perf events"); | |
237 | return -1; | |
238 | } | |
239 | ||
240 | return 0; | |
241 | } | |
242 | ||
243 | static void sigill_handler(int signr, siginfo_t *info, void *unused) | |
244 | { | |
245 | static int warned = 0; | |
246 | ucontext_t *ctx = (ucontext_t *)unused; | |
247 | unsigned long *pc = &UCONTEXT_NIA(ctx); | |
248 | ||
1936f094 NR |
249 | /* mtspr 3,RS to check for move to DSCR below */ |
250 | if ((*((unsigned int *)*pc) & 0xfc1fffff) == 0x7c0303a6) { | |
d2bf7932 NR |
251 | if (!warned++) |
252 | printf("WARNING: Skipping over dscr setup. Consider running 'ppc64_cpu --dscr=1' manually.\n"); | |
253 | *pc += 4; | |
254 | } else { | |
255 | printf("SIGILL at %p\n", pc); | |
256 | abort(); | |
257 | } | |
258 | } | |
259 | ||
260 | void set_dscr(unsigned long val) | |
261 | { | |
262 | static int init = 0; | |
263 | struct sigaction sa; | |
264 | ||
265 | if (!init) { | |
266 | memset(&sa, 0, sizeof(sa)); | |
267 | sa.sa_sigaction = sigill_handler; | |
268 | sa.sa_flags = SA_SIGINFO; | |
269 | if (sigaction(SIGILL, &sa, NULL)) | |
270 | perror("sigill_handler"); | |
271 | init = 1; | |
272 | } | |
273 | ||
1936f094 | 274 | asm volatile("mtspr %1,%0" : : "r" (val), "i" (SPRN_DSCR)); |
d2bf7932 | 275 | } |