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