1 /*
2 * Copyright (c) 2011 Alex Hornung <alex@alexhornung.com>.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in
13 * the documentation and/or other materials provided with the
14 * distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
19 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
20 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
22 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
26 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/kernel.h>
33 #include <sys/proc.h>
34 #include <sys/conf.h>
35 #include <sys/malloc.h>
36 #include <machine/md_var.h>
37 #include <sys/ctype.h>
38 #include <sys/kthread.h>
39 #include <sys/device.h>
40 #include <sys/fcntl.h>
41 #include <sys/module.h>
42 #include <libprop/proplib.h>
43 #include <sys/tbridge.h>
44
45 static cdev_t tbridge_dev;
46 static d_open_t tbridge_dev_open;
47 static d_close_t tbridge_dev_close;
48 static d_ioctl_t tbridge_dev_ioctl;
49
50
51 static struct dev_ops tbridge_dev_ops = {
52 { "tbridge", 0, 0 },
53 .d_open = tbridge_dev_open,
54 .d_close = tbridge_dev_close,
55 .d_ioctl = tbridge_dev_ioctl
56 };
57
58 static struct tbridge_testcase *tbridge_curtest = NULL;
59 static prop_dictionary_t tbridge_testcase = NULL;
60 static int tbridge_result_ready = 0;
61 static char tbridge_msgbuf[131072]; /* 128 kB message buffer */
62 static char *tbridge_msgbuf_ptr;
63 size_t tbridge_msgbuf_remsz;
64
65 static prop_dictionary_t
testcase_get_result_dict(prop_dictionary_t testcase)66 testcase_get_result_dict(prop_dictionary_t testcase)
67 {
68 prop_dictionary_t result_dict;
69 int r;
70
71 result_dict = prop_dictionary_get(testcase, "result");
72 if (result_dict == NULL) {
73 result_dict = prop_dictionary_create();
74 if (result_dict == NULL) {
75 kprintf("could not allocate new result dict");
76 return NULL;
77 }
78
79 r = prop_dictionary_set(testcase, "result", result_dict);
80 if (r == 0) {
81 kprintf("prop_dictionary operation failed");
82 return NULL;
83 }
84 }
85
86 return result_dict;
87 }
88
89 static int
testcase_get_timeout(prop_dictionary_t testcase)90 testcase_get_timeout(prop_dictionary_t testcase)
91 {
92 int32_t val;
93 int r;
94
95 r = prop_dictionary_get_int32(prop_dictionary_get(testcase, "opts"),
96 "timeout_in_secs", &val);
97 if (r == 0)
98 val = 600/* default timeout = 10min */;
99
100 return val;
101 }
102
103 static int
testcase_set_stdout_buf(prop_dictionary_t testcase,const char * buf)104 testcase_set_stdout_buf(prop_dictionary_t testcase, const char *buf)
105 {
106 prop_dictionary_t dict = testcase_get_result_dict(testcase);
107
108 return !prop_dictionary_set_cstring(dict, "stdout_buf", buf);
109 }
110
111 static int
testcase_set_result(prop_dictionary_t testcase,int result)112 testcase_set_result(prop_dictionary_t testcase, int result)
113 {
114 prop_dictionary_t dict = testcase_get_result_dict(testcase);
115
116 return !prop_dictionary_set_int32(dict, "result", result);
117 }
118
119 static const char *
testcase_get_name(prop_dictionary_t testcase)120 testcase_get_name(prop_dictionary_t testcase)
121 {
122 const char *str;
123 int r;
124
125 r = prop_dictionary_get_cstring_nocopy(testcase, "name", &str);
126 if (r == 0)
127 str = "???";
128
129 return str;
130 }
131
132 int
tbridge_printf(const char * fmt,...)133 tbridge_printf(const char *fmt, ...)
134 {
135 __va_list args;
136 int i;
137
138 __va_start(args,fmt);
139 i = kvsnprintf(tbridge_msgbuf_ptr, tbridge_msgbuf_remsz, fmt, args);
140 __va_end(args);
141
142 tbridge_msgbuf_ptr += i;
143 tbridge_msgbuf_remsz -= i;
144
145 return i;
146 }
147
148 static int
tbridge_register(struct tbridge_testcase * test)149 tbridge_register(struct tbridge_testcase *test)
150 {
151 if (tbridge_curtest != NULL)
152 return EBUSY;
153
154 tbridge_curtest = test;
155
156 return 0;
157 }
158
159 static int
tbridge_unregister(void)160 tbridge_unregister(void)
161 {
162 tbridge_curtest = NULL;
163
164 return 0;
165 }
166
167 int
tbridge_testcase_modhandler(module_t mod,int type,void * arg)168 tbridge_testcase_modhandler(module_t mod, int type, void *arg)
169 {
170 struct tbridge_testcase *tcase = (struct tbridge_testcase *)arg;
171 int error = 0;
172
173 switch (type) {
174 case MOD_LOAD:
175 error = tbridge_register(tcase);
176 break;
177
178 case MOD_UNLOAD:
179 if (tcase->tb_abort != NULL)
180 error = tcase->tb_abort();
181
182 if (!error)
183 tbridge_unregister();
184 break;
185
186 default:
187 break;
188 }
189
190 return error;
191 }
192
193 void
tbridge_test_done(int result)194 tbridge_test_done(int result)
195 {
196 KKASSERT(tbridge_testcase != NULL);
197
198 kprintf("testcase '%s' called test_done with result=%d\n",
199 testcase_get_name(tbridge_testcase), result);
200
201 testcase_set_result(tbridge_testcase, result);
202 testcase_set_stdout_buf(tbridge_testcase, tbridge_msgbuf);
203
204 tbridge_result_ready = 1;
205 wakeup(&tbridge_result_ready);
206 }
207
208 static void
tbridge_reset(void)209 tbridge_reset(void)
210 {
211 tbridge_msgbuf_ptr = tbridge_msgbuf;
212 tbridge_msgbuf_remsz = sizeof(tbridge_msgbuf);
213
214 if (tbridge_testcase != NULL) {
215 prop_object_release(tbridge_testcase);
216 tbridge_testcase = NULL;
217 }
218
219 safemem_reset_error_count();
220 tbridge_result_ready = 0;
221 }
222
223
224 /*
225 * dev stuff
226 */
227 static int
tbridge_dev_open(struct dev_open_args * ap)228 tbridge_dev_open(struct dev_open_args *ap)
229 {
230 return 0;
231 }
232
233 static int
tbridge_dev_close(struct dev_close_args * ap)234 tbridge_dev_close(struct dev_close_args *ap)
235 {
236 return 0;
237 }
238
239 static int
tbridge_dev_ioctl(struct dev_ioctl_args * ap)240 tbridge_dev_ioctl(struct dev_ioctl_args *ap)
241 {
242 struct plistref *pref;
243 struct thread *td;
244 int error, r;
245
246 error = 0;
247
248 /* Use proplib(3) for userspace/kernel communication */
249 pref = (struct plistref *)ap->a_data;
250
251 switch(ap->a_cmd) {
252 case TBRIDGE_LOADTEST:
253 tbridge_reset();
254
255 if (tbridge_curtest == NULL)
256 return EINVAL;
257
258 error = prop_dictionary_copyin_ioctl(pref, ap->a_cmd,
259 &tbridge_testcase);
260 if (error)
261 return error;
262
263 break;
264
265 case TBRIDGE_GETRESULT:
266 error = kthread_create(tbridge_curtest->tb_run, NULL, &td,
267 "testrunner");
268 if (error)
269 tbridge_test_done(RESULT_PREFAIL);
270
271 /* The following won't be called if the thread wasn't created */
272 if (!tbridge_result_ready) {
273 r = tsleep(&tbridge_result_ready, 0, "tbridgeres",
274 hz * testcase_get_timeout(tbridge_testcase));
275 if (r != 0) {
276 if (tbridge_curtest->tb_abort != NULL)
277 tbridge_curtest->tb_abort();
278
279 tbridge_test_done(RESULT_TIMEOUT);
280 }
281 }
282
283 if (safemem_get_error_count() != 0) {
284 /*
285 * If there were any double-frees or buffer over- or
286 * underflows, we mark the result as 'signalled', i.e.
287 * the equivalent of a SIGSEGV/SIGBUS in userland.
288 */
289 testcase_set_result(tbridge_testcase, RESULT_SIGNALLED);
290 }
291
292 error = prop_dictionary_copyout_ioctl(pref, ap->a_cmd,
293 tbridge_testcase);
294 if (error)
295 return error;
296
297 break;
298 default:
299 error = ENOTTY; /* Inappropriate ioctl for device */
300 break;
301 }
302
303 return(error);
304 }
305
306
307 static int
testbridge_modevent(module_t mod,int type,void * unused)308 testbridge_modevent(module_t mod, int type, void *unused)
309 {
310 switch (type) {
311 case MOD_LOAD:
312 tbridge_dev = make_dev(&tbridge_dev_ops,
313 0,
314 UID_ROOT,
315 GID_WHEEL,
316 0600,
317 "tbridge");
318
319 tbridge_testcase = NULL;
320 tbridge_reset();
321
322 kprintf("dfregress kernel test bridge ready!\n");
323 return 0;
324
325 case MOD_UNLOAD:
326 destroy_dev(tbridge_dev);
327 kprintf("dfregress kernel test bridge unloaded.\n");
328 return 0;
329 }
330
331 return EINVAL;
332 }
333
334 static moduledata_t testbridge_mod = {
335 "testbridge",
336 testbridge_modevent,
337 0
338 };
339
340 DECLARE_MODULE(testbridge, testbridge_mod, SI_SUB_DRIVERS, SI_ORDER_ANY);
341 MODULE_VERSION(testbridge, 1);
342