xref: /xnu-10002.41.9/tests/ecc_test.c (revision 699cd48037512bf4380799317ca44ca453c82f57)
1 #include <unistd.h>
2 #include <stdio.h>
3 #include <signal.h>
4 
5 #include <darwintest.h>
6 #include <darwintest_utils.h>
7 
8 /*
9  * We're going to inject ECC errors into shared library text, so don't
10  * run with other tests.
11  */
12 T_GLOBAL_META(T_META_RUN_CONCURRENTLY(false),
13     T_META_OWNER("josephb_22"),
14     T_META_NAMESPACE("xnu.vm"),
15     T_META_RADAR_COMPONENT_NAME("xnu"),
16     T_META_RADAR_COMPONENT_VERSION("VM"));
17 
18 /*
19  * No system(3c) on watchOS, so provide our own.
20  * returns -1 if fails to run
21  * returns 0 if process exits normally.
22  * returns +n if process exits due to signal N
23  */
24 static int
my_system(const char * command,const char * arg)25 my_system(const char *command, const char *arg)
26 {
27 	pid_t pid;
28 	int status = 0;
29 	int signal = 0;
30 	int ret;
31 	const char *argv[] = {
32 		command,
33 		/* "-v",           uncomment if debugging the tests */
34 		arg,
35 		NULL
36 	};
37 
38 	if (dt_launch_tool(&pid, (char **)(void *)argv, FALSE, NULL, NULL)) {
39 		return -1;
40 	}
41 
42 	ret = dt_waitpid(pid, &status, &signal, 100);
43 	if (signal != 0) {
44 		return signal;
45 	} else if (status != 0) {
46 		return status;
47 	}
48 	return 0;
49 }
50 
51 
52 /*
53  * The tests are run in the following order:
54  *
55  * - call foo (i.e. private text page)
56  * - Inject ECC error into foo, then call foo
57  *
58  * - call atan (i.e. shared text page)
59  * - inject ecc error into atan, then call atan
60  *
61  * atan() was picked as a shared region function that isn't likely used by any normal daemons.
62  *
63  * - reference to clean data page
64  * - reference to clean data page with injected error
65  *
66  * - reference to dirty data page
67  * - reference to dirty data page with injected error
68  *
69  * - copyout to page
70  * - copyout to a page with injected error
71  */
72 static void
test_body(bool corrected)73 test_body(bool corrected)
74 {
75 	int ret;
76 
77 	/*
78 	 * test of process text page
79 	 */
80 	ret = my_system("./ecc_test_helper", "foo");
81 	T_QUIET; T_ASSERT_EQ(ret, 0, "First call of foo");
82 
83 	ret = my_system("./ecc_test_helper", "Xfoo");
84 	T_QUIET; T_ASSERT_EQ(ret, 0, "Failed to recover from ECC to clean app text page");
85 
86 	ret = my_system("./ecc_test_helper", "foo");
87 	T_QUIET; T_ASSERT_EQ(ret, 0, "Fixed call of foo");
88 
89 	/*
90 	 * test of shared library text page
91 	 */
92 	ret = my_system("./ecc_test_helper", "atan");
93 	T_QUIET; T_ASSERT_EQ(ret, 0, "First call of atan");
94 
95 	ret = my_system("./ecc_test_helper", "Xatan");
96 	T_QUIET; T_ASSERT_EQ(ret, 0, "Failed to recover from ECC to clean shared region page");
97 
98 	ret = my_system("./ecc_test_helper", "atan");
99 	T_QUIET; T_ASSERT_EQ(ret, 0, "Fixed call of atan");
100 
101 	/*
102 	 * test of clean data page
103 	 */
104 	ret = my_system("./ecc_test_helper", "clean");
105 	T_QUIET; T_ASSERT_EQ(ret, 0, "First call of clean");
106 
107 	ret = my_system("./ecc_test_helper", "Xclean");
108 	T_QUIET; T_ASSERT_EQ(ret, 0, "Failed to recover from ECC to clean page");
109 
110 	ret = my_system("./ecc_test_helper", "clean");
111 	T_QUIET; T_ASSERT_EQ(ret, 0, "Fixed call of clean");
112 
113 	/*
114 	 * test of dirty data page
115 	 */
116 	ret = my_system("./ecc_test_helper", "Xdirty");
117 	if (corrected) {
118 		T_QUIET; T_ASSERT_EQ(ret, 0, "Corrected ECC read of dirty failed");
119 	} else {
120 		T_QUIET; T_ASSERT_NE(ret, 0, "Read of Uncorrected ECC dirty data didn't fail");
121 	}
122 
123 	/*
124 	 * test of ecc during copyout
125 	 */
126 	ret = my_system("./ecc_test_helper", "Xcopyout");
127 	if (corrected) {
128 		T_QUIET; T_ASSERT_EQ(ret, 0, "Corrected ECC copyout failed");
129 	} else {
130 		T_QUIET; T_ASSERT_NE(ret, 0, "Uncorrected ECC copyout didn't fail"); /* not recoverable */
131 	}
132 }
133 
134 T_DECL(ecc_uncorrected_test, "test detection/recovery from ECC uncorrected errors",
135     T_META_IGNORECRASHES(".*ecc_test_helper.*"),
136     T_META_ASROOT(true),
137     T_META_ENABLED(FALSE))  /* once other support lands, change to T_META_ENABLED(TARGET_CPU_ARM64 && TARGET_OS_OSX) */
138 {
139 	int err;
140 	uint value = 0;
141 	size_t s = sizeof value;
142 
143 	/*
144 	 * Only run on systems which support retired pages.
145 	 */
146 	err = sysctlbyname("vm.retired_pages_count", &value, &s, NULL, 0);
147 	if (err) {
148 		T_SKIP("ECC not supported");
149 	}
150 
151 	/*
152 	 * Set testing mode to acc
153 	 */
154 	value = 0;
155 	err = sysctlbyname("vm.test_ecc_dcs", NULL, NULL, &value, s);
156 	if (err) {
157 		T_SKIP("Failed to clear dcs mode");
158 	}
159 
160 	/*
161 	 * Set testing mode to uncorrected.
162 	 */
163 	value = 0;
164 	err = sysctlbyname("vm.test_corrected_ecc", NULL, NULL, &value, s);
165 	if (err) {
166 		T_SKIP("Failed to set uncorrected mode");
167 	}
168 
169 	test_body(false);
170 }
171 
172 T_DECL(ecc_corrected_test, "test detection/recovery from ECC corrected errors",
173     T_META_IGNORECRASHES(".*ecc_test_helper.*"),
174     T_META_ASROOT(true),
175     T_META_ENABLED(FALSE))  /* once other support lands, change to T_META_ENABLED(TARGET_CPU_ARM64 && TARGET_OS_OSX) */
176 {
177 	int err;
178 	uint value = 0;
179 	size_t s = sizeof value;
180 
181 	/*
182 	 * Only run on systems which support retired pages.
183 	 */
184 	err = sysctlbyname("vm.retired_pages_count", &value, &s, NULL, 0);
185 	if (err) {
186 		T_SKIP("ECC not supported");
187 	}
188 
189 	/*
190 	 * Set testing mode to acc
191 	 */
192 	value = 0;
193 	err = sysctlbyname("vm.test_ecc_dcs", NULL, NULL, &value, s);
194 	if (err) {
195 		T_SKIP("Failed to clear dcs mode");
196 	}
197 
198 	/*
199 	 * Set testing mode to corrected.
200 	 */
201 	value = 1;
202 	err = sysctlbyname("vm.test_corrected_ecc", NULL, NULL, &value, s);
203 	if (err) {
204 		T_SKIP("Failed to set corrected mode");
205 	}
206 
207 	test_body(true);
208 }
209 
210 T_DECL(dcs_uncorrected_test, "test detection/recovery from ECC uncorrected errors via dcs",
211     T_META_IGNORECRASHES(".*ecc_test_helper.*"),
212     T_META_ASROOT(true),
213     T_META_ENABLED(FALSE))  /* once other support lands, change to T_META_ENABLED(TARGET_CPU_ARM64 && TARGET_OS_OSX) */
214 {
215 	int err;
216 	uint value = 0;
217 	size_t s = sizeof value;
218 
219 	/*
220 	 * Only run on systems which support retired pages.
221 	 */
222 	err = sysctlbyname("vm.retired_pages_count", &value, &s, NULL, 0);
223 	if (err) {
224 		T_SKIP("ECC not supported");
225 	}
226 
227 	/*
228 	 * Set testing mode to dcs
229 	 */
230 	value = 1;
231 	err = sysctlbyname("vm.test_ecc_dcs", NULL, NULL, &value, s);
232 	if (err) {
233 		T_SKIP("Failed to set dcs mode");
234 	}
235 
236 	/*
237 	 * Set testing mode to uncorrected.
238 	 */
239 	value = 0;
240 	err = sysctlbyname("vm.test_corrected_ecc", NULL, NULL, &value, s);
241 	if (err) {
242 		T_SKIP("Failed to set uncorrected mode");
243 	}
244 
245 	test_body(false);
246 }
247 
248 T_DECL(dcs_corrected_test, "test detection/recovery from ECC corrected errors via dcs",
249     T_META_IGNORECRASHES(".*ecc_test_helper.*"),
250     T_META_ASROOT(true),
251     T_META_ENABLED(FALSE))  /* once other support lands, change to T_META_ENABLED(TARGET_CPU_ARM64 && TARGET_OS_OSX) */
252 {
253 	int err;
254 	uint value = 0;
255 	size_t s = sizeof value;
256 
257 	/*
258 	 * Only run on systems which support retired pages.
259 	 */
260 	err = sysctlbyname("vm.retired_pages_count", &value, &s, NULL, 0);
261 	if (err) {
262 		T_SKIP("ECC not supported");
263 	}
264 
265 	/*
266 	 * Set testing mode to dcs
267 	 */
268 	value = 1;
269 	err = sysctlbyname("vm.test_ecc_dcs", NULL, NULL, &value, s);
270 	if (err) {
271 		T_SKIP("Failed to set dcs mode");
272 	}
273 
274 	/*
275 	 * Set testing mode to corrected.
276 	 */
277 	value = 1;
278 	err = sysctlbyname("vm.test_corrected_ecc", NULL, NULL, &value, s);
279 	if (err) {
280 		T_SKIP("Failed to set corrected mode");
281 	}
282 
283 	test_body(true);
284 }
285