1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright 2013-2015, Michael Ellerman, IBM Corp. |
4 | */ |
5 | |
6 | #define _GNU_SOURCE /* For CPU_ZERO etc. */ |
7 | |
8 | #include <elf.h> |
9 | #include <errno.h> |
10 | #include <fcntl.h> |
11 | #include <inttypes.h> |
12 | #include <limits.h> |
13 | #include <link.h> |
14 | #include <sched.h> |
15 | #include <stdio.h> |
16 | #include <stdlib.h> |
17 | #include <string.h> |
18 | #include <sys/ioctl.h> |
19 | #include <sys/stat.h> |
20 | #include <sys/sysinfo.h> |
21 | #include <sys/types.h> |
22 | #include <sys/utsname.h> |
23 | #include <unistd.h> |
24 | #include <asm/unistd.h> |
25 | #include <linux/limits.h> |
26 | |
27 | #include "utils.h" |
28 | |
29 | static char auxv[4096]; |
30 | |
31 | int read_file(const char *path, char *buf, size_t count, size_t *len) |
32 | { |
33 | ssize_t rc; |
34 | int fd; |
35 | int err; |
36 | char eof; |
37 | |
38 | fd = open(path, O_RDONLY); |
39 | if (fd < 0) |
40 | return -errno; |
41 | |
42 | rc = read(fd, buf, count); |
43 | if (rc < 0) { |
44 | err = -errno; |
45 | goto out; |
46 | } |
47 | |
48 | if (len) |
49 | *len = rc; |
50 | |
51 | /* Overflow if there are still more bytes after filling the buffer */ |
52 | if (rc == count) { |
53 | rc = read(fd, &eof, 1); |
54 | if (rc != 0) { |
55 | err = -EOVERFLOW; |
56 | goto out; |
57 | } |
58 | } |
59 | |
60 | err = 0; |
61 | |
62 | out: |
63 | close(fd); |
64 | errno = -err; |
65 | return err; |
66 | } |
67 | |
68 | int read_file_alloc(const char *path, char **buf, size_t *len) |
69 | { |
70 | size_t read_offset = 0; |
71 | size_t buffer_len = 0; |
72 | char *buffer = NULL; |
73 | int err; |
74 | int fd; |
75 | |
76 | fd = open(path, O_RDONLY); |
77 | if (fd < 0) |
78 | return -errno; |
79 | |
80 | /* |
81 | * We don't use stat & preallocate st_size because some non-files |
82 | * report 0 file size. Instead just dynamically grow the buffer |
83 | * as needed. |
84 | */ |
85 | while (1) { |
86 | ssize_t rc; |
87 | |
88 | if (read_offset >= buffer_len / 2) { |
89 | char *next_buffer; |
90 | |
91 | buffer_len = buffer_len ? buffer_len * 2 : 4096; |
92 | next_buffer = realloc(buffer, buffer_len); |
93 | if (!next_buffer) { |
94 | err = -errno; |
95 | goto out; |
96 | } |
97 | buffer = next_buffer; |
98 | } |
99 | |
100 | rc = read(fd, buffer + read_offset, buffer_len - read_offset); |
101 | if (rc < 0) { |
102 | err = -errno; |
103 | goto out; |
104 | } |
105 | |
106 | if (rc == 0) |
107 | break; |
108 | |
109 | read_offset += rc; |
110 | } |
111 | |
112 | *buf = buffer; |
113 | if (len) |
114 | *len = read_offset; |
115 | |
116 | err = 0; |
117 | |
118 | out: |
119 | close(fd); |
120 | if (err) |
121 | free(buffer); |
122 | errno = -err; |
123 | return err; |
124 | } |
125 | |
126 | int write_file(const char *path, const char *buf, size_t count) |
127 | { |
128 | int fd; |
129 | int err; |
130 | ssize_t rc; |
131 | |
132 | fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644); |
133 | if (fd < 0) |
134 | return -errno; |
135 | |
136 | rc = write(fd, buf, count); |
137 | if (rc < 0) { |
138 | err = -errno; |
139 | goto out; |
140 | } |
141 | |
142 | if (rc != count) { |
143 | err = -EOVERFLOW; |
144 | goto out; |
145 | } |
146 | |
147 | err = 0; |
148 | |
149 | out: |
150 | close(fd); |
151 | errno = -err; |
152 | return err; |
153 | } |
154 | |
155 | int read_auxv(char *buf, ssize_t buf_size) |
156 | { |
157 | int err; |
158 | |
159 | err = read_file(path: "/proc/self/auxv" , buf, count: buf_size, NULL); |
160 | if (err) { |
161 | perror("Error reading /proc/self/auxv" ); |
162 | return err; |
163 | } |
164 | |
165 | return 0; |
166 | } |
167 | |
168 | int read_debugfs_file(const char *subpath, char *buf, size_t count) |
169 | { |
170 | char path[PATH_MAX] = "/sys/kernel/debug/" ; |
171 | |
172 | strncat(path, subpath, sizeof(path) - strlen(path) - 1); |
173 | |
174 | return read_file(path, buf, count, NULL); |
175 | } |
176 | |
177 | int write_debugfs_file(const char *subpath, const char *buf, size_t count) |
178 | { |
179 | char path[PATH_MAX] = "/sys/kernel/debug/" ; |
180 | |
181 | strncat(path, subpath, sizeof(path) - strlen(path) - 1); |
182 | |
183 | return write_file(path, buf, count); |
184 | } |
185 | |
186 | static int validate_int_parse(const char *buffer, size_t count, char *end) |
187 | { |
188 | int err = 0; |
189 | |
190 | /* Require at least one digit */ |
191 | if (end == buffer) { |
192 | err = -EINVAL; |
193 | goto out; |
194 | } |
195 | |
196 | /* Require all remaining characters be whitespace-ish */ |
197 | for (; end < buffer + count; end++) { |
198 | if (*end == '\0') |
199 | break; |
200 | |
201 | if (*end != ' ' && *end != '\n') { |
202 | err = -EINVAL; |
203 | goto out; |
204 | } |
205 | } |
206 | |
207 | out: |
208 | errno = -err; |
209 | return err; |
210 | } |
211 | |
212 | static int parse_bounded_int(const char *buffer, size_t count, intmax_t *result, |
213 | int base, intmax_t min, intmax_t max) |
214 | { |
215 | int err; |
216 | char *end; |
217 | |
218 | errno = 0; |
219 | *result = strtoimax(buffer, &end, base); |
220 | |
221 | if (errno) |
222 | return -errno; |
223 | |
224 | err = validate_int_parse(buffer, count, end); |
225 | if (err) |
226 | goto out; |
227 | |
228 | if (*result < min || *result > max) |
229 | err = -EOVERFLOW; |
230 | |
231 | out: |
232 | errno = -err; |
233 | return err; |
234 | } |
235 | |
236 | static int parse_bounded_uint(const char *buffer, size_t count, uintmax_t *result, |
237 | int base, uintmax_t max) |
238 | { |
239 | int err = 0; |
240 | char *end; |
241 | |
242 | errno = 0; |
243 | *result = strtoumax(buffer, &end, base); |
244 | |
245 | if (errno) |
246 | return -errno; |
247 | |
248 | err = validate_int_parse(buffer, count, end); |
249 | if (err) |
250 | goto out; |
251 | |
252 | if (*result > max) |
253 | err = -EOVERFLOW; |
254 | |
255 | out: |
256 | errno = -err; |
257 | return err; |
258 | } |
259 | |
260 | int parse_intmax(const char *buffer, size_t count, intmax_t *result, int base) |
261 | { |
262 | return parse_bounded_int(buffer, count, result, base, INTMAX_MIN, INTMAX_MAX); |
263 | } |
264 | |
265 | int parse_uintmax(const char *buffer, size_t count, uintmax_t *result, int base) |
266 | { |
267 | return parse_bounded_uint(buffer, count, result, base, UINTMAX_MAX); |
268 | } |
269 | |
270 | int parse_int(const char *buffer, size_t count, int *result, int base) |
271 | { |
272 | intmax_t parsed; |
273 | int err = parse_bounded_int(buffer, count, &parsed, base, INT_MIN, INT_MAX); |
274 | |
275 | *result = parsed; |
276 | return err; |
277 | } |
278 | |
279 | int parse_uint(const char *buffer, size_t count, unsigned int *result, int base) |
280 | { |
281 | uintmax_t parsed; |
282 | int err = parse_bounded_uint(buffer, count, &parsed, base, UINT_MAX); |
283 | |
284 | *result = parsed; |
285 | return err; |
286 | } |
287 | |
288 | int parse_long(const char *buffer, size_t count, long *result, int base) |
289 | { |
290 | intmax_t parsed; |
291 | int err = parse_bounded_int(buffer, count, &parsed, base, LONG_MIN, LONG_MAX); |
292 | |
293 | *result = parsed; |
294 | return err; |
295 | } |
296 | |
297 | int parse_ulong(const char *buffer, size_t count, unsigned long *result, int base) |
298 | { |
299 | uintmax_t parsed; |
300 | int err = parse_bounded_uint(buffer, count, &parsed, base, ULONG_MAX); |
301 | |
302 | *result = parsed; |
303 | return err; |
304 | } |
305 | |
306 | int read_long(const char *path, long *result, int base) |
307 | { |
308 | int err; |
309 | char buffer[32] = {0}; |
310 | |
311 | err = read_file(path, buf: buffer, count: sizeof(buffer) - 1, NULL); |
312 | if (err) |
313 | return err; |
314 | |
315 | return parse_long(buffer, count: sizeof(buffer), result, base); |
316 | } |
317 | |
318 | int read_ulong(const char *path, unsigned long *result, int base) |
319 | { |
320 | int err; |
321 | char buffer[32] = {0}; |
322 | |
323 | err = read_file(path, buf: buffer, count: sizeof(buffer) - 1, NULL); |
324 | if (err) |
325 | return err; |
326 | |
327 | return parse_ulong(buffer, count: sizeof(buffer), result, base); |
328 | } |
329 | |
330 | int write_long(const char *path, long result, int base) |
331 | { |
332 | int err; |
333 | int len; |
334 | char buffer[32]; |
335 | |
336 | /* Decimal only for now: no format specifier for signed hex values */ |
337 | if (base != 10) { |
338 | err = -EINVAL; |
339 | goto out; |
340 | } |
341 | |
342 | len = snprintf(buffer, sizeof(buffer), "%ld" , result); |
343 | if (len < 0 || len >= sizeof(buffer)) { |
344 | err = -EOVERFLOW; |
345 | goto out; |
346 | } |
347 | |
348 | err = write_file(path, buf: buffer, count: len); |
349 | |
350 | out: |
351 | errno = -err; |
352 | return err; |
353 | } |
354 | |
355 | int write_ulong(const char *path, unsigned long result, int base) |
356 | { |
357 | int err; |
358 | int len; |
359 | char buffer[32]; |
360 | char *fmt; |
361 | |
362 | switch (base) { |
363 | case 10: |
364 | fmt = "%lu" ; |
365 | break; |
366 | case 16: |
367 | fmt = "%lx" ; |
368 | break; |
369 | default: |
370 | err = -EINVAL; |
371 | goto out; |
372 | } |
373 | |
374 | len = snprintf(buffer, sizeof(buffer), fmt, result); |
375 | if (len < 0 || len >= sizeof(buffer)) { |
376 | err = -errno; |
377 | goto out; |
378 | } |
379 | |
380 | err = write_file(path, buf: buffer, count: len); |
381 | |
382 | out: |
383 | errno = -err; |
384 | return err; |
385 | } |
386 | |
387 | void *find_auxv_entry(int type, char *auxv) |
388 | { |
389 | ElfW(auxv_t) *p; |
390 | |
391 | p = (ElfW(auxv_t) *)auxv; |
392 | |
393 | while (p->a_type != AT_NULL) { |
394 | if (p->a_type == type) |
395 | return p; |
396 | |
397 | p++; |
398 | } |
399 | |
400 | return NULL; |
401 | } |
402 | |
403 | void *get_auxv_entry(int type) |
404 | { |
405 | ElfW(auxv_t) *p; |
406 | |
407 | if (read_auxv(buf: auxv, buf_size: sizeof(auxv))) |
408 | return NULL; |
409 | |
410 | p = find_auxv_entry(type, auxv); |
411 | if (p) |
412 | return (void *)p->a_un.a_val; |
413 | |
414 | return NULL; |
415 | } |
416 | |
417 | int pick_online_cpu(void) |
418 | { |
419 | int ncpus, cpu = -1; |
420 | cpu_set_t *mask; |
421 | size_t size; |
422 | |
423 | ncpus = get_nprocs_conf(); |
424 | size = CPU_ALLOC_SIZE(ncpus); |
425 | mask = CPU_ALLOC(ncpus); |
426 | if (!mask) { |
427 | perror("malloc" ); |
428 | return -1; |
429 | } |
430 | |
431 | CPU_ZERO_S(size, mask); |
432 | |
433 | if (sched_getaffinity(0, size, mask)) { |
434 | perror("sched_getaffinity" ); |
435 | goto done; |
436 | } |
437 | |
438 | /* We prefer a primary thread, but skip 0 */ |
439 | for (cpu = 8; cpu < ncpus; cpu += 8) |
440 | if (CPU_ISSET_S(cpu, size, mask)) |
441 | goto done; |
442 | |
443 | /* Search for anything, but in reverse */ |
444 | for (cpu = ncpus - 1; cpu >= 0; cpu--) |
445 | if (CPU_ISSET_S(cpu, size, mask)) |
446 | goto done; |
447 | |
448 | printf("No cpus in affinity mask?!\n" ); |
449 | |
450 | done: |
451 | CPU_FREE(mask); |
452 | return cpu; |
453 | } |
454 | |
455 | int bind_to_cpu(int cpu) |
456 | { |
457 | cpu_set_t mask; |
458 | int err; |
459 | |
460 | if (cpu == BIND_CPU_ANY) { |
461 | cpu = pick_online_cpu(); |
462 | if (cpu < 0) |
463 | return cpu; |
464 | } |
465 | |
466 | printf("Binding to cpu %d\n" , cpu); |
467 | |
468 | CPU_ZERO(&mask); |
469 | CPU_SET(cpu, &mask); |
470 | |
471 | err = sched_setaffinity(0, sizeof(mask), &mask); |
472 | if (err) |
473 | return err; |
474 | |
475 | return cpu; |
476 | } |
477 | |
478 | bool is_ppc64le(void) |
479 | { |
480 | struct utsname uts; |
481 | int rc; |
482 | |
483 | errno = 0; |
484 | rc = uname(&uts); |
485 | if (rc) { |
486 | perror("uname" ); |
487 | return false; |
488 | } |
489 | |
490 | return strcmp(uts.machine, "ppc64le" ) == 0; |
491 | } |
492 | |
493 | int read_sysfs_file(char *fpath, char *result, size_t result_size) |
494 | { |
495 | char path[PATH_MAX] = "/sys/" ; |
496 | |
497 | strncat(path, fpath, PATH_MAX - strlen(path) - 1); |
498 | |
499 | return read_file(path, buf: result, count: result_size, NULL); |
500 | } |
501 | |
502 | int read_debugfs_int(const char *debugfs_file, int *result) |
503 | { |
504 | int err; |
505 | char value[16] = {0}; |
506 | |
507 | err = read_debugfs_file(subpath: debugfs_file, buf: value, count: sizeof(value) - 1); |
508 | if (err) |
509 | return err; |
510 | |
511 | return parse_int(buffer: value, count: sizeof(value), result, base: 10); |
512 | } |
513 | |
514 | int write_debugfs_int(const char *debugfs_file, int result) |
515 | { |
516 | char value[16]; |
517 | |
518 | snprintf(value, 16, "%d" , result); |
519 | |
520 | return write_debugfs_file(subpath: debugfs_file, buf: value, count: strlen(value)); |
521 | } |
522 | |
523 | static long perf_event_open(struct perf_event_attr *hw_event, pid_t pid, |
524 | int cpu, int group_fd, unsigned long flags) |
525 | { |
526 | return syscall(__NR_perf_event_open, hw_event, pid, cpu, |
527 | group_fd, flags); |
528 | } |
529 | |
530 | static void perf_event_attr_init(struct perf_event_attr *event_attr, |
531 | unsigned int type, |
532 | unsigned long config) |
533 | { |
534 | memset(event_attr, 0, sizeof(*event_attr)); |
535 | |
536 | event_attr->type = type; |
537 | event_attr->size = sizeof(struct perf_event_attr); |
538 | event_attr->config = config; |
539 | event_attr->read_format = PERF_FORMAT_GROUP; |
540 | event_attr->disabled = 1; |
541 | event_attr->exclude_kernel = 1; |
542 | event_attr->exclude_hv = 1; |
543 | event_attr->exclude_guest = 1; |
544 | } |
545 | |
546 | int perf_event_open_counter(unsigned int type, |
547 | unsigned long config, int group_fd) |
548 | { |
549 | int fd; |
550 | struct perf_event_attr event_attr; |
551 | |
552 | perf_event_attr_init(event_attr: &event_attr, type, config); |
553 | |
554 | fd = perf_event_open(hw_event: &event_attr, pid: 0, cpu: -1, group_fd, flags: 0); |
555 | |
556 | if (fd < 0) |
557 | perror("perf_event_open() failed" ); |
558 | |
559 | return fd; |
560 | } |
561 | |
562 | int perf_event_enable(int fd) |
563 | { |
564 | if (ioctl(fd, PERF_EVENT_IOC_ENABLE, PERF_IOC_FLAG_GROUP) == -1) { |
565 | perror("error while enabling perf events" ); |
566 | return -1; |
567 | } |
568 | |
569 | return 0; |
570 | } |
571 | |
572 | int perf_event_disable(int fd) |
573 | { |
574 | if (ioctl(fd, PERF_EVENT_IOC_DISABLE, PERF_IOC_FLAG_GROUP) == -1) { |
575 | perror("error disabling perf events" ); |
576 | return -1; |
577 | } |
578 | |
579 | return 0; |
580 | } |
581 | |
582 | int perf_event_reset(int fd) |
583 | { |
584 | if (ioctl(fd, PERF_EVENT_IOC_RESET, PERF_IOC_FLAG_GROUP) == -1) { |
585 | perror("error resetting perf events" ); |
586 | return -1; |
587 | } |
588 | |
589 | return 0; |
590 | } |
591 | |
592 | int using_hash_mmu(bool *using_hash) |
593 | { |
594 | char line[128]; |
595 | FILE *f; |
596 | int rc; |
597 | |
598 | f = fopen("/proc/cpuinfo" , "r" ); |
599 | FAIL_IF(!f); |
600 | |
601 | rc = 0; |
602 | while (fgets(line, sizeof(line), f) != NULL) { |
603 | if (!strcmp(line, "MMU : Hash\n" ) || |
604 | !strcmp(line, "platform : Cell\n" ) || |
605 | !strcmp(line, "platform : PowerMac\n" )) { |
606 | *using_hash = true; |
607 | goto out; |
608 | } |
609 | |
610 | if (strcmp(line, "MMU : Radix\n" ) == 0) { |
611 | *using_hash = false; |
612 | goto out; |
613 | } |
614 | } |
615 | |
616 | rc = -1; |
617 | out: |
618 | fclose(f); |
619 | return rc; |
620 | } |
621 | |
622 | struct sigaction push_signal_handler(int sig, void (*fn)(int, siginfo_t *, void *)) |
623 | { |
624 | struct sigaction sa; |
625 | struct sigaction old_handler; |
626 | |
627 | sa.sa_sigaction = fn; |
628 | sigemptyset(&sa.sa_mask); |
629 | sa.sa_flags = SA_SIGINFO; |
630 | FAIL_IF_EXIT_MSG(sigaction(sig, &sa, &old_handler), |
631 | "failed to push signal handler" ); |
632 | |
633 | return old_handler; |
634 | } |
635 | |
636 | struct sigaction pop_signal_handler(int sig, struct sigaction old_handler) |
637 | { |
638 | struct sigaction popped; |
639 | |
640 | FAIL_IF_EXIT_MSG(sigaction(sig, &old_handler, &popped), |
641 | "failed to pop signal handler" ); |
642 | |
643 | return popped; |
644 | } |
645 | |