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 <machine/varargs.h> 42 #include <sys/module.h> 43 #include <libprop/proplib.h> 44 #include <sys/tbridge.h> 45 46 static cdev_t tbridge_dev; 47 static d_open_t tbridge_dev_open; 48 static d_close_t tbridge_dev_close; 49 static d_ioctl_t tbridge_dev_ioctl; 50 51 52 static struct dev_ops tbridge_dev_ops = { 53 { "tbridge", 0, 0 }, 54 .d_open = tbridge_dev_open, 55 .d_close = tbridge_dev_close, 56 .d_ioctl = tbridge_dev_ioctl 57 }; 58 59 static struct tbridge_testcase *tbridge_curtest = NULL; 60 static prop_dictionary_t tbridge_testcase = NULL; 61 static int tbridge_result_ready = 0; 62 static char tbridge_msgbuf[131072]; /* 128 kB message buffer */ 63 static char *tbridge_msgbuf_ptr; 64 size_t tbridge_msgbuf_remsz; 65 66 static prop_dictionary_t 67 testcase_get_result_dict(prop_dictionary_t testcase) 68 { 69 prop_dictionary_t result_dict; 70 int r; 71 72 result_dict = prop_dictionary_get(testcase, "result"); 73 if (result_dict == NULL) { 74 result_dict = prop_dictionary_create(); 75 if (result_dict == NULL) { 76 kprintf("could not allocate new result dict"); 77 return NULL; 78 } 79 80 r = prop_dictionary_set(testcase, "result", result_dict); 81 if (r == 0) { 82 kprintf("prop_dictionary operation failed"); 83 return NULL; 84 } 85 } 86 87 return result_dict; 88 } 89 90 static int 91 testcase_get_timeout(prop_dictionary_t testcase) 92 { 93 int32_t val; 94 int r; 95 96 r = prop_dictionary_get_int32(prop_dictionary_get(testcase, "opts"), 97 "timeout_in_secs", &val); 98 if (r == 0) 99 val = 600/* default timeout = 10min */; 100 101 return val; 102 } 103 104 static int 105 testcase_set_stdout_buf(prop_dictionary_t testcase, const char *buf) 106 { 107 prop_dictionary_t dict = testcase_get_result_dict(testcase); 108 109 return !prop_dictionary_set_cstring(dict, "stdout_buf", buf); 110 } 111 112 static int 113 testcase_set_result(prop_dictionary_t testcase, int result) 114 { 115 prop_dictionary_t dict = testcase_get_result_dict(testcase); 116 117 return !prop_dictionary_set_int32(dict, "result", result); 118 } 119 120 static const char * 121 testcase_get_name(prop_dictionary_t testcase) 122 { 123 const char *str; 124 int r; 125 126 r = prop_dictionary_get_cstring_nocopy(testcase, "name", &str); 127 if (r == 0) 128 str = "???"; 129 130 return str; 131 } 132 133 int 134 tbridge_printf(const char *fmt, ...) 135 { 136 __va_list args; 137 int i; 138 139 __va_start(args,fmt); 140 i = kvsnprintf(tbridge_msgbuf_ptr, tbridge_msgbuf_remsz, fmt, args); 141 __va_end(args); 142 143 tbridge_msgbuf_ptr += i; 144 tbridge_msgbuf_remsz -= i; 145 146 return i; 147 } 148 149 static int 150 tbridge_register(struct tbridge_testcase *test) 151 { 152 if (tbridge_curtest != NULL) 153 return EBUSY; 154 155 tbridge_curtest = test; 156 157 return 0; 158 } 159 160 static int 161 tbridge_unregister(void) 162 { 163 tbridge_curtest = NULL; 164 165 return 0; 166 } 167 168 int 169 tbridge_testcase_modhandler(module_t mod, int type, void *arg) 170 { 171 struct tbridge_testcase *tcase = (struct tbridge_testcase *)arg; 172 int error = 0; 173 174 switch (type) { 175 case MOD_LOAD: 176 error = tbridge_register(tcase); 177 break; 178 179 case MOD_UNLOAD: 180 if (tcase->tb_abort != NULL) 181 error = tcase->tb_abort(); 182 183 if (!error) 184 tbridge_unregister(); 185 break; 186 187 default: 188 break; 189 } 190 191 return error; 192 } 193 194 void 195 tbridge_test_done(int result) 196 { 197 KKASSERT(tbridge_testcase != NULL); 198 199 kprintf("testcase '%s' called test_done with result=%d\n", 200 testcase_get_name(tbridge_testcase), result); 201 202 testcase_set_result(tbridge_testcase, result); 203 testcase_set_stdout_buf(tbridge_testcase, tbridge_msgbuf); 204 205 tbridge_result_ready = 1; 206 wakeup(&tbridge_result_ready); 207 } 208 209 static void 210 tbridge_reset(void) 211 { 212 tbridge_msgbuf_ptr = tbridge_msgbuf; 213 tbridge_msgbuf_remsz = sizeof(tbridge_msgbuf); 214 215 if (tbridge_testcase != NULL) { 216 prop_object_release(tbridge_testcase); 217 tbridge_testcase = NULL; 218 } 219 220 safemem_reset_error_count(); 221 tbridge_result_ready = 0; 222 } 223 224 225 /* 226 * dev stuff 227 */ 228 static int 229 tbridge_dev_open(struct dev_open_args *ap) 230 { 231 return 0; 232 } 233 234 static int 235 tbridge_dev_close(struct dev_close_args *ap) 236 { 237 return 0; 238 } 239 240 static int 241 tbridge_dev_ioctl(struct dev_ioctl_args *ap) 242 { 243 struct plistref *pref; 244 struct thread *td; 245 int error, r; 246 247 error = 0; 248 249 /* Use proplib(3) for userspace/kernel communication */ 250 pref = (struct plistref *)ap->a_data; 251 252 switch(ap->a_cmd) { 253 case TBRIDGE_LOADTEST: 254 tbridge_reset(); 255 256 if (tbridge_curtest == NULL) 257 return EINVAL; 258 259 error = prop_dictionary_copyin_ioctl(pref, ap->a_cmd, 260 &tbridge_testcase); 261 if (error) 262 return error; 263 264 break; 265 266 case TBRIDGE_GETRESULT: 267 error = kthread_create(tbridge_curtest->tb_run, NULL, &td, 268 "testrunner"); 269 if (error) 270 tbridge_test_done(RESULT_PREFAIL); 271 272 /* The following won't be called if the thread wasn't created */ 273 if (!tbridge_result_ready) { 274 r = tsleep(&tbridge_result_ready, 0, "tbridgeres", 275 hz * testcase_get_timeout(tbridge_testcase)); 276 if (r != 0) { 277 if (tbridge_curtest->tb_abort != NULL) 278 tbridge_curtest->tb_abort(); 279 280 tbridge_test_done(RESULT_TIMEOUT); 281 } 282 } 283 284 if (safemem_get_error_count() != 0) { 285 /* 286 * If there were any double-frees or buffer over- or 287 * underflows, we mark the result as 'signalled', i.e. 288 * the equivalent of a SIGSEGV/SIGBUS in userland. 289 */ 290 testcase_set_result(tbridge_testcase, RESULT_SIGNALLED); 291 } 292 293 error = prop_dictionary_copyout_ioctl(pref, ap->a_cmd, 294 tbridge_testcase); 295 if (error) 296 return error; 297 298 break; 299 default: 300 error = ENOTTY; /* Inappropriate ioctl for device */ 301 break; 302 } 303 304 return(error); 305 } 306 307 308 static int 309 testbridge_modevent(module_t mod, int type, void *unused) 310 { 311 switch (type) { 312 case MOD_LOAD: 313 tbridge_dev = make_dev(&tbridge_dev_ops, 314 0, 315 UID_ROOT, 316 GID_WHEEL, 317 0600, 318 "tbridge"); 319 320 tbridge_testcase = NULL; 321 tbridge_reset(); 322 323 kprintf("dfregress kernel test bridge ready!\n"); 324 return 0; 325 326 case MOD_UNLOAD: 327 destroy_dev(tbridge_dev); 328 kprintf("dfregress kernel test bridge unloaded.\n"); 329 return 0; 330 } 331 332 return EINVAL; 333 } 334 335 static moduledata_t testbridge_mod = { 336 "testbridge", 337 testbridge_modevent, 338 0 339 }; 340 341 DECLARE_MODULE(testbridge, testbridge_mod, SI_SUB_DRIVERS, SI_ORDER_ANY); 342 MODULE_VERSION(testbridge, 1); 343