1 /*
2  * Copyright (C) 2016 Google, Inc.
3  *
4  * This software is licensed under the terms of the GNU General Public
5  * License version 2, as published by the Free Software Foundation, and
6  * may be copied, distributed, and modified under those terms.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  * GNU General Public License for more details.
12  *
13  */
14 
15 #define _GNU_SOURCE
16 
17 #include <errno.h>
18 #include <fcntl.h>
19 #include <sched.h>
20 #include <signal.h>
21 #include <stdbool.h>
22 #include <stdio.h>
23 #include <string.h>
24 #include <unistd.h>
25 #include <sys/ptrace.h>
26 #include <sys/stat.h>
27 #include <sys/timerfd.h>
28 #include <sys/types.h>
29 #include <sys/wait.h>
30 
31 #include "../kselftest.h"
32 
33 void child(int cpu)
34 {
35 	cpu_set_t set;
36 
37 	CPU_ZERO(&set);
38 	CPU_SET(cpu, &set);
39 	if (sched_setaffinity(0, sizeof(set), &set) != 0) {
40 		perror("sched_setaffinity() failed");
41 		_exit(1);
42 	}
43 
44 	if (ptrace(PTRACE_TRACEME, 0, NULL, NULL) != 0) {
45 		perror("ptrace(PTRACE_TRACEME) failed");
46 		_exit(1);
47 	}
48 
49 	if (raise(SIGSTOP) != 0) {
50 		perror("raise(SIGSTOP) failed");
51 		_exit(1);
52 	}
53 
54 	_exit(0);
55 }
56 
57 bool run_test(int cpu)
58 {
59 	int status;
60 	pid_t pid = fork();
61 	pid_t wpid;
62 
63 	if (pid < 0) {
64 		perror("fork() failed");
65 		return false;
66 	}
67 	if (pid == 0)
68 		child(cpu);
69 
70 	wpid = waitpid(pid, &status, __WALL);
71 	if (wpid != pid) {
72 		perror("waitpid() failed");
73 		return false;
74 	}
75 	if (!WIFSTOPPED(status)) {
76 		printf("child did not stop\n");
77 		return false;
78 	}
79 	if (WSTOPSIG(status) != SIGSTOP) {
80 		printf("child did not stop with SIGSTOP\n");
81 		return false;
82 	}
83 
84 	if (ptrace(PTRACE_SINGLESTEP, pid, NULL, NULL) < 0) {
85 		if (errno == EIO) {
86 			printf("ptrace(PTRACE_SINGLESTEP) not supported on this architecture\n");
87 			ksft_exit_skip();
88 		}
89 		perror("ptrace(PTRACE_SINGLESTEP) failed");
90 		return false;
91 	}
92 
93 	wpid = waitpid(pid, &status, __WALL);
94 	if (wpid != pid) {
95 		perror("waitpid() failed");
96 		return false;
97 	}
98 	if (WIFEXITED(status)) {
99 		printf("child did not single-step\n");
100 		return false;
101 	}
102 	if (!WIFSTOPPED(status)) {
103 		printf("child did not stop\n");
104 		return false;
105 	}
106 	if (WSTOPSIG(status) != SIGTRAP) {
107 		printf("child did not stop with SIGTRAP\n");
108 		return false;
109 	}
110 
111 	if (ptrace(PTRACE_CONT, pid, NULL, NULL) < 0) {
112 		perror("ptrace(PTRACE_CONT) failed");
113 		return false;
114 	}
115 
116 	wpid = waitpid(pid, &status, __WALL);
117 	if (wpid != pid) {
118 		perror("waitpid() failed");
119 		return false;
120 	}
121 	if (!WIFEXITED(status)) {
122 		printf("child did not exit after PTRACE_CONT\n");
123 		return false;
124 	}
125 
126 	return true;
127 }
128 
129 void suspend(void)
130 {
131 	int power_state_fd;
132 	struct sigevent event = {};
133 	int timerfd;
134 	int err;
135 	struct itimerspec spec = {};
136 
137 	power_state_fd = open("/sys/power/state", O_RDWR);
138 	if (power_state_fd < 0) {
139 		perror("open(\"/sys/power/state\") failed (is this test running as root?)");
140 		ksft_exit_fail();
141 	}
142 
143 	timerfd = timerfd_create(CLOCK_BOOTTIME_ALARM, 0);
144 	if (timerfd < 0) {
145 		perror("timerfd_create() failed");
146 		ksft_exit_fail();
147 	}
148 
149 	spec.it_value.tv_sec = 5;
150 	err = timerfd_settime(timerfd, 0, &spec, NULL);
151 	if (err < 0) {
152 		perror("timerfd_settime() failed");
153 		ksft_exit_fail();
154 	}
155 
156 	if (write(power_state_fd, "mem", strlen("mem")) != strlen("mem")) {
157 		perror("entering suspend failed");
158 		ksft_exit_fail();
159 	}
160 
161 	close(timerfd);
162 	close(power_state_fd);
163 }
164 
165 int main(int argc, char **argv)
166 {
167 	int opt;
168 	bool do_suspend = true;
169 	bool succeeded = true;
170 	cpu_set_t available_cpus;
171 	int err;
172 	int cpu;
173 
174 	while ((opt = getopt(argc, argv, "n")) != -1) {
175 		switch (opt) {
176 		case 'n':
177 			do_suspend = false;
178 			break;
179 		default:
180 			printf("Usage: %s [-n]\n", argv[0]);
181 			printf("        -n: do not trigger a suspend/resume cycle before the test\n");
182 			return -1;
183 		}
184 	}
185 
186 	if (do_suspend)
187 		suspend();
188 
189 	err = sched_getaffinity(0, sizeof(available_cpus), &available_cpus);
190 	if (err < 0) {
191 		perror("sched_getaffinity() failed");
192 		ksft_exit_fail();
193 	}
194 
195 	for (cpu = 0; cpu < CPU_SETSIZE; cpu++) {
196 		bool test_success;
197 
198 		if (!CPU_ISSET(cpu, &available_cpus))
199 			continue;
200 
201 		test_success = run_test(cpu);
202 		printf("CPU %d: ", cpu);
203 		if (test_success) {
204 			printf("[OK]\n");
205 			ksft_inc_pass_cnt();
206 		} else {
207 			printf("[FAILED]\n");
208 			ksft_inc_fail_cnt();
209 			succeeded = false;
210 		}
211 	}
212 
213 	ksft_print_cnts();
214 	if (succeeded)
215 		ksft_exit_pass();
216 	else
217 		ksft_exit_fail();
218 }
219