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