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