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