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 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 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 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 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 * 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 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 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 160 tbridge_unregister(void) 161 { 162 tbridge_curtest = NULL; 163 164 return 0; 165 } 166 167 int 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 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 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 228 tbridge_dev_open(struct dev_open_args *ap) 229 { 230 return 0; 231 } 232 233 static int 234 tbridge_dev_close(struct dev_close_args *ap) 235 { 236 return 0; 237 } 238 239 static int 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 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