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
read_file(const char * path,char * buf,size_t count,size_t * len)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
read_file_alloc(const char * path,char ** buf,size_t * len)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
write_file(const char * path,const char * buf,size_t count)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
read_auxv(char * buf,ssize_t buf_size)155 int read_auxv(char *buf, ssize_t buf_size)
156 {
157 int err;
158
159 err = read_file("/proc/self/auxv", buf, buf_size, NULL);
160 if (err) {
161 perror("Error reading /proc/self/auxv");
162 return err;
163 }
164
165 return 0;
166 }
167
read_debugfs_file(const char * subpath,char * buf,size_t count)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
write_debugfs_file(const char * subpath,const char * buf,size_t count)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
validate_int_parse(const char * buffer,size_t count,char * end)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
parse_bounded_int(const char * buffer,size_t count,intmax_t * result,int base,intmax_t min,intmax_t max)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
parse_bounded_uint(const char * buffer,size_t count,uintmax_t * result,int base,uintmax_t max)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
parse_intmax(const char * buffer,size_t count,intmax_t * result,int base)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
parse_uintmax(const char * buffer,size_t count,uintmax_t * result,int base)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
parse_int(const char * buffer,size_t count,int * result,int base)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
parse_uint(const char * buffer,size_t count,unsigned int * result,int base)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
parse_long(const char * buffer,size_t count,long * result,int base)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
parse_ulong(const char * buffer,size_t count,unsigned long * result,int base)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
read_long(const char * path,long * result,int base)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, buffer, sizeof(buffer) - 1, NULL);
312 if (err)
313 return err;
314
315 return parse_long(buffer, sizeof(buffer), result, base);
316 }
317
read_ulong(const char * path,unsigned long * result,int base)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, buffer, sizeof(buffer) - 1, NULL);
324 if (err)
325 return err;
326
327 return parse_ulong(buffer, sizeof(buffer), result, base);
328 }
329
write_long(const char * path,long result,int base)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, buffer, len);
349
350 out:
351 errno = -err;
352 return err;
353 }
354
write_ulong(const char * path,unsigned long result,int base)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, buffer, len);
381
382 out:
383 errno = -err;
384 return err;
385 }
386
find_auxv_entry(int type,char * auxv)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
get_auxv_entry(int type)403 void *get_auxv_entry(int type)
404 {
405 ElfW(auxv_t) *p;
406
407 if (read_auxv(auxv, 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
pick_online_cpu(void)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
is_ppc64le(void)455 bool is_ppc64le(void)
456 {
457 struct utsname uts;
458 int rc;
459
460 errno = 0;
461 rc = uname(&uts);
462 if (rc) {
463 perror("uname");
464 return false;
465 }
466
467 return strcmp(uts.machine, "ppc64le") == 0;
468 }
469
read_sysfs_file(char * fpath,char * result,size_t result_size)470 int read_sysfs_file(char *fpath, char *result, size_t result_size)
471 {
472 char path[PATH_MAX] = "/sys/";
473
474 strncat(path, fpath, PATH_MAX - strlen(path) - 1);
475
476 return read_file(path, result, result_size, NULL);
477 }
478
read_debugfs_int(const char * debugfs_file,int * result)479 int read_debugfs_int(const char *debugfs_file, int *result)
480 {
481 int err;
482 char value[16] = {0};
483
484 err = read_debugfs_file(debugfs_file, value, sizeof(value) - 1);
485 if (err)
486 return err;
487
488 return parse_int(value, sizeof(value), result, 10);
489 }
490
write_debugfs_int(const char * debugfs_file,int result)491 int write_debugfs_int(const char *debugfs_file, int result)
492 {
493 char value[16];
494
495 snprintf(value, 16, "%d", result);
496
497 return write_debugfs_file(debugfs_file, value, strlen(value));
498 }
499
perf_event_open(struct perf_event_attr * hw_event,pid_t pid,int cpu,int group_fd,unsigned long flags)500 static long perf_event_open(struct perf_event_attr *hw_event, pid_t pid,
501 int cpu, int group_fd, unsigned long flags)
502 {
503 return syscall(__NR_perf_event_open, hw_event, pid, cpu,
504 group_fd, flags);
505 }
506
perf_event_attr_init(struct perf_event_attr * event_attr,unsigned int type,unsigned long config)507 static void perf_event_attr_init(struct perf_event_attr *event_attr,
508 unsigned int type,
509 unsigned long config)
510 {
511 memset(event_attr, 0, sizeof(*event_attr));
512
513 event_attr->type = type;
514 event_attr->size = sizeof(struct perf_event_attr);
515 event_attr->config = config;
516 event_attr->read_format = PERF_FORMAT_GROUP;
517 event_attr->disabled = 1;
518 event_attr->exclude_kernel = 1;
519 event_attr->exclude_hv = 1;
520 event_attr->exclude_guest = 1;
521 }
522
perf_event_open_counter(unsigned int type,unsigned long config,int group_fd)523 int perf_event_open_counter(unsigned int type,
524 unsigned long config, int group_fd)
525 {
526 int fd;
527 struct perf_event_attr event_attr;
528
529 perf_event_attr_init(&event_attr, type, config);
530
531 fd = perf_event_open(&event_attr, 0, -1, group_fd, 0);
532
533 if (fd < 0)
534 perror("perf_event_open() failed");
535
536 return fd;
537 }
538
perf_event_enable(int fd)539 int perf_event_enable(int fd)
540 {
541 if (ioctl(fd, PERF_EVENT_IOC_ENABLE, PERF_IOC_FLAG_GROUP) == -1) {
542 perror("error while enabling perf events");
543 return -1;
544 }
545
546 return 0;
547 }
548
perf_event_disable(int fd)549 int perf_event_disable(int fd)
550 {
551 if (ioctl(fd, PERF_EVENT_IOC_DISABLE, PERF_IOC_FLAG_GROUP) == -1) {
552 perror("error disabling perf events");
553 return -1;
554 }
555
556 return 0;
557 }
558
perf_event_reset(int fd)559 int perf_event_reset(int fd)
560 {
561 if (ioctl(fd, PERF_EVENT_IOC_RESET, PERF_IOC_FLAG_GROUP) == -1) {
562 perror("error resetting perf events");
563 return -1;
564 }
565
566 return 0;
567 }
568
using_hash_mmu(bool * using_hash)569 int using_hash_mmu(bool *using_hash)
570 {
571 char line[128];
572 FILE *f;
573 int rc;
574
575 f = fopen("/proc/cpuinfo", "r");
576 FAIL_IF(!f);
577
578 rc = 0;
579 while (fgets(line, sizeof(line), f) != NULL) {
580 if (!strcmp(line, "MMU : Hash\n") ||
581 !strcmp(line, "platform : Cell\n") ||
582 !strcmp(line, "platform : PowerMac\n")) {
583 *using_hash = true;
584 goto out;
585 }
586
587 if (strcmp(line, "MMU : Radix\n") == 0) {
588 *using_hash = false;
589 goto out;
590 }
591 }
592
593 rc = -1;
594 out:
595 fclose(f);
596 return rc;
597 }
598