1 /*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12 /*
13 * Copyright 2025 Oxide Computer Company
14 * Copyright 2024 Ryan Zezeski
15 */
16
17 #include <stdlib.h>
18 #include <stdio.h>
19 #include <unistd.h>
20 #include <errno.h>
21 #include <fcntl.h>
22 #include <string.h>
23 #include <dirent.h>
24 #include <atomic.h>
25 #include <sys/ktest.h>
26 #include <sys/systeminfo.h>
27 #include <sys/modctl.h>
28 #include <upanic.h>
29
30 #include "libktest_impl.h"
31
32 /*
33 * Open ktest device handle.
34 *
35 * Returns handle pointer on success, otherwise NULL (setting errno).
36 */
37 ktest_hdl_t *
ktest_init(void)38 ktest_init(void)
39 {
40 int fd = open(KTEST_DEV_PATH, O_RDONLY | O_CLOEXEC, 0);
41 if (fd < 0) {
42 return (NULL);
43 }
44
45 ktest_hdl_t *hdl = malloc(sizeof (ktest_hdl_t));
46 if (hdl == NULL) {
47 const int err = errno;
48 (void) close(fd);
49 errno = err;
50 return (NULL);
51 }
52
53 hdl->kt_fd = fd;
54 return (hdl);
55 }
56
57 /*
58 * Close ktest device handle.
59 */
60 void
ktest_fini(ktest_hdl_t * hdl)61 ktest_fini(ktest_hdl_t *hdl)
62 {
63 if (hdl == NULL) {
64 return;
65 }
66 if (hdl->kt_fd >= 0) {
67 (void) close(hdl->kt_fd);
68 hdl->kt_fd = -1;
69 }
70 free(hdl);
71 }
72
73 #define DEFAULT_LIST_BUF_SZ (64 * 1024)
74
75 static nvlist_t *
ktest_query_tests(ktest_hdl_t * hdl)76 ktest_query_tests(ktest_hdl_t *hdl)
77 {
78 char *resp = malloc(DEFAULT_LIST_BUF_SZ);
79 ktest_list_op_t op = {
80 .klo_resp = resp,
81 .klo_resp_len = DEFAULT_LIST_BUF_SZ,
82 };
83
84 if (resp == NULL) {
85 return (NULL);
86 }
87
88 int ret;
89 ret = ioctl(hdl->kt_fd, KTEST_IOCTL_LIST_TESTS, &op);
90
91 /* Resize buffer and retry, if ioctl indicates it was too small */
92 if (ret == -1 && errno == ENOBUFS) {
93 free(resp);
94
95 if ((resp = malloc(op.klo_resp_len)) == NULL) {
96 return (NULL);
97 }
98 op.klo_resp = resp;
99 ret = ioctl(hdl->kt_fd, KTEST_IOCTL_LIST_TESTS, &op);
100 }
101 if (ret == -1) {
102 free(resp);
103 return (NULL);
104 }
105
106 nvlist_t *tests = NULL;
107 if ((ret = nvlist_unpack(resp, op.klo_resp_len, &tests, 0)) != 0) {
108 free(resp);
109 errno = ret;
110 return (NULL);
111 }
112 free(resp);
113
114 /*
115 * Verify that the response is marked with the expected serialization
116 * format. Remove the nvpair so that only the modules remain.
117 */
118 uint64_t vsn = 0;
119 if (nvlist_lookup_uint64(tests, KTEST_SER_FMT_KEY, &vsn) != 0 ||
120 vsn != KTEST_SER_FMT_VSN) {
121 nvlist_free(tests);
122 errno = EINVAL;
123 return (NULL);
124 }
125 fnvlist_remove(tests, KTEST_SER_FMT_KEY);
126
127 return (tests);
128 }
129
130 static void
ktest_iter_next_test(ktest_list_iter_t * iter,bool do_reset)131 ktest_iter_next_test(ktest_list_iter_t *iter, bool do_reset)
132 {
133 if (iter->kli_test == NULL && !do_reset) {
134 return;
135 }
136 if (iter->kli_tests == NULL) {
137 return;
138 }
139
140 iter->kli_test = nvlist_next_nvpair(iter->kli_tests, iter->kli_test);
141 while (iter->kli_test != NULL) {
142 boolean_t requires_input;
143 if (nvpair_type(iter->kli_test) == DATA_TYPE_NVLIST &&
144 nvlist_lookup_boolean_value(
145 fnvpair_value_nvlist(iter->kli_test), KTEST_TEST_INPUT_KEY,
146 &requires_input) == 0) {
147 iter->kli_req_input = requires_input;
148 break;
149 }
150 iter->kli_test =
151 nvlist_next_nvpair(iter->kli_tests, iter->kli_test);
152 }
153 }
154
155 static void
ktest_iter_next_suite(ktest_list_iter_t * iter,bool do_reset)156 ktest_iter_next_suite(ktest_list_iter_t *iter, bool do_reset)
157 {
158 if (iter->kli_suite == NULL && !do_reset) {
159 return;
160 }
161 if (iter->kli_suites == NULL) {
162 return;
163 }
164
165 iter->kli_suite = nvlist_next_nvpair(iter->kli_suites, iter->kli_suite);
166 iter->kli_tests = NULL;
167 iter->kli_test = NULL;
168 while (iter->kli_suite != NULL) {
169 if (nvpair_type(iter->kli_suite) == DATA_TYPE_NVLIST &&
170 nvlist_lookup_nvlist(fnvpair_value_nvlist(iter->kli_suite),
171 KTEST_SUITE_TESTS_KEY, &iter->kli_tests) == 0) {
172 break;
173 }
174
175 iter->kli_suite = nvlist_next_nvpair(iter->kli_suites,
176 iter->kli_suite);
177 }
178
179 ktest_iter_next_test(iter, true);
180 }
181
182 static void
ktest_iter_next_module(ktest_list_iter_t * iter,bool do_reset)183 ktest_iter_next_module(ktest_list_iter_t *iter, bool do_reset)
184 {
185 if (iter->kli_module == NULL && !do_reset) {
186 return;
187 }
188 VERIFY(iter->kli_modules != NULL);
189
190 iter->kli_module = nvlist_next_nvpair(iter->kli_modules,
191 iter->kli_module);
192 iter->kli_suites = NULL;
193 iter->kli_suite = NULL;
194
195 while (iter->kli_module != NULL) {
196 if (nvpair_type(iter->kli_module) == DATA_TYPE_NVLIST &&
197 nvlist_lookup_nvlist(fnvpair_value_nvlist(iter->kli_module),
198 KTEST_MODULE_SUITES_KEY, &iter->kli_suites) == 0) {
199 break;
200 }
201
202 iter->kli_module =
203 nvlist_next_nvpair(iter->kli_modules, iter->kli_module);
204 }
205
206 ktest_iter_next_suite(iter, true);
207 }
208
209 /*
210 * List currently available ktests.
211 *
212 * Returns test list iterator on success, otherwise NULL (setting errno).
213 */
214 ktest_list_iter_t *
ktest_list(ktest_hdl_t * hdl)215 ktest_list(ktest_hdl_t *hdl)
216 {
217 nvlist_t *tests = ktest_query_tests(hdl);
218
219 if (tests == NULL) {
220 return (NULL);
221 }
222
223 ktest_list_iter_t *iter = malloc(sizeof (ktest_list_iter_t));
224 if (iter == NULL) {
225 const int err = errno;
226 nvlist_free(tests);
227 errno = err;
228 return (NULL);
229 }
230
231 iter->kli_hdl = hdl;
232 iter->kli_modules = tests;
233 iter->kli_module = NULL;
234 ktest_iter_next_module(iter, true);
235
236 return (iter);
237 }
238
239 /*
240 * Get the next ktest entry from a test list iterator.
241 *
242 * Returns true an item was available from the iterator (populating entry),
243 * otherwise false.
244 */
245 bool
ktest_list_next(ktest_list_iter_t * iter,ktest_entry_t * entry)246 ktest_list_next(ktest_list_iter_t *iter, ktest_entry_t *entry)
247 {
248 while (iter->kli_module != NULL) {
249 if (iter->kli_test != NULL) {
250 /*
251 * Output the current test, and move iterator to the
252 * next in preparation for a subsequent call.
253 */
254 entry->ke_module = nvpair_name(iter->kli_module);
255 entry->ke_suite = nvpair_name(iter->kli_suite);
256 entry->ke_test = nvpair_name(iter->kli_test);
257 entry->ke_requires_input = iter->kli_req_input;
258 ktest_iter_next_test(iter, false);
259 return (true);
260 }
261
262 ktest_iter_next_suite(iter, false);
263 if (iter->kli_suite != NULL) {
264 continue;
265 }
266 ktest_iter_next_module(iter, false);
267 }
268 return (false);
269 }
270
271 /*
272 * Reset ktest list iterator to its beginning.
273 */
274 void
ktest_list_reset(ktest_list_iter_t * iter)275 ktest_list_reset(ktest_list_iter_t *iter)
276 {
277 iter->kli_module = NULL;
278 ktest_iter_next_module(iter, true);
279 }
280
281 /*
282 * Free a ktest list iterator.
283 */
284 void
ktest_list_free(ktest_list_iter_t * iter)285 ktest_list_free(ktest_list_iter_t *iter)
286 {
287 if (iter == NULL) {
288 return;
289 }
290 iter->kli_test = iter->kli_suite = iter->kli_module = NULL;
291 if (iter->kli_modules != NULL) {
292 nvlist_free(iter->kli_modules);
293 iter->kli_modules = NULL;
294 }
295 free(iter);
296 }
297
298 /*
299 * Run a ktest.
300 *
301 * Requests that the ktest module run a ktest specified by module/suite/test
302 * triple in the ktest_run_req_t. If the test requires input, that too is
303 * expected to be set in the run request.
304 *
305 * If the test was able to be run (regardless of its actual result), and the
306 * emitted results data processed, ktest_run() will return true. Any message
307 * output from the test will be placed in krr_msg, which the caller is
308 * responsible for free()-ing.
309 *
310 * If an error was encountered while attempting to run the test, or process its
311 * results, ktest_run() will return false, and the ktest_run_result_t will not
312 * be populated.
313 */
314 bool
ktest_run(ktest_hdl_t * hdl,const ktest_run_req_t * req,ktest_run_result_t * res)315 ktest_run(ktest_hdl_t *hdl, const ktest_run_req_t *req, ktest_run_result_t *res)
316 {
317 ktest_run_op_t kro = {
318 .kro_input_bytes = req->krq_input,
319 .kro_input_len = req->krq_input_len,
320 };
321
322 (void) strncpy(kro.kro_module, req->krq_module,
323 sizeof (kro.kro_module));
324 (void) strncpy(kro.kro_suite, req->krq_suite, sizeof (kro.kro_suite));
325 (void) strncpy(kro.kro_test, req->krq_test, sizeof (kro.kro_test));
326
327 if (ioctl(hdl->kt_fd, KTEST_IOCTL_RUN_TEST, &kro) == -1) {
328 return (false);
329 }
330
331 const ktest_result_t *kres = &kro.kro_result;
332 res->krr_code = (ktest_code_t)kres->kr_type;
333 res->krr_line = (uint_t)kres->kr_line;
334
335 const size_t msg_len =
336 strnlen(kres->kr_msg_prepend, sizeof (kres->kr_msg_prepend)) +
337 strnlen(kres->kr_msg, sizeof (kres->kr_msg));
338
339 if (msg_len != 0) {
340 if (asprintf(&res->krr_msg, "%s%s", kres->kr_msg_prepend,
341 kres->kr_msg) == -1) {
342 return (false);
343 }
344 } else {
345 res->krr_msg = NULL;
346 }
347
348 return (true);
349 }
350
351
352 /*
353 * Get the string name for a ktest_code_t.
354 */
355 const char *
ktest_code_name(ktest_code_t code)356 ktest_code_name(ktest_code_t code)
357 {
358 switch (code) {
359 case KTEST_CODE_NONE:
360 return ("NONE");
361 case KTEST_CODE_PASS:
362 return ("PASS");
363 case KTEST_CODE_FAIL:
364 return ("FAIL");
365 case KTEST_CODE_SKIP:
366 return ("SKIP");
367 case KTEST_CODE_ERROR:
368 return ("ERROR");
369 default:
370 break;
371 }
372 const char errmsg[] = "unexpected ktest_code value";
373 upanic(errmsg, sizeof (errmsg));
374 }
375
376 #define KTEST_MODULE_SUFFIX "_ktest"
377 #define KTEST_BASE_MODULE_DIR "/usr/kernel/misc/ktest"
378
379 static char *ktest_cached_module_dir;
380
381 static const char *
ktest_mod_directory(void)382 ktest_mod_directory(void)
383 {
384 if (ktest_cached_module_dir != NULL) {
385 return (ktest_cached_module_dir);
386 }
387
388 char archbuf[20];
389 if (sysinfo(SI_ARCHITECTURE_64, archbuf, sizeof (archbuf)) < 0) {
390 return (NULL);
391 }
392
393 char *path = NULL;
394 if (asprintf(&path, "%s/%s", KTEST_BASE_MODULE_DIR, archbuf) < 0) {
395 return (NULL);
396 }
397
398 char *old = atomic_cas_ptr(&ktest_cached_module_dir, NULL, path);
399 if (old == NULL) {
400 return (path);
401 } else {
402 free(path);
403 return (ktest_cached_module_dir);
404 }
405 }
406
407 static bool
ktest_mod_path(const char * name,char * buf)408 ktest_mod_path(const char *name, char *buf)
409 {
410 const char *base = ktest_mod_directory();
411 if (base == NULL) {
412 return (false);
413 }
414
415 (void) snprintf(buf, MAXPATHLEN, "%s/%s" KTEST_MODULE_SUFFIX, base,
416 name);
417 return (true);
418 }
419
420 static int
ktest_mod_id_for_name(const char * name)421 ktest_mod_id_for_name(const char *name)
422 {
423 struct modinfo modinfo = {
424 .mi_info = MI_INFO_ONE | MI_INFO_BY_NAME,
425 };
426 (void) snprintf(modinfo.mi_name, sizeof (modinfo.mi_name),
427 "%s" KTEST_MODULE_SUFFIX, name);
428
429 if (modctl(MODINFO, 0, &modinfo) < 0) {
430 return (-1);
431 }
432 return (modinfo.mi_id);
433 }
434
435 /*
436 * Attempt to load a test module.
437 *
438 * Returns true if a ktests for the module could be loaded (or were already so),
439 * otherwise false (setting errno).
440 */
441 bool
ktest_mod_load(const char * name)442 ktest_mod_load(const char *name)
443 {
444 if (ktest_mod_id_for_name(name) > 0) {
445 /* Module is already loaded */
446 return (true);
447 }
448
449 char path[MAXPATHLEN];
450 if (!ktest_mod_path(name, path)) {
451 return (false);
452 }
453
454 int id = 0;
455 if (modctl(MODLOAD, 0, path, &id) != 0) {
456 return (false);
457 }
458 return (true);
459 }
460
461 /*
462 * Attempt to unload a test module.
463 */
464 void
ktest_mod_unload(const char * name)465 ktest_mod_unload(const char *name)
466 {
467 const int id = ktest_mod_id_for_name(name);
468 if (id > 0) {
469 (void) modctl(MODUNLOAD, id);
470 }
471 }
472
473 static bool
ktest_mod_for_each(void (* cb)(const char *))474 ktest_mod_for_each(void (*cb)(const char *))
475 {
476 const char *dpath;
477
478 if ((dpath = ktest_mod_directory()) == NULL) {
479 return (false);
480 }
481
482 DIR *dp = opendir(dpath);
483 if (dp == NULL) {
484 return (false);
485 }
486 struct dirent *de;
487 while ((de = readdir(dp)) != NULL) {
488 char *suffix = strrchr(de->d_name, '_');
489
490 if (suffix == NULL ||
491 strcmp(suffix, KTEST_MODULE_SUFFIX) != 0) {
492 continue;
493 }
494 /*
495 * Drop the suffix from the name, and confirm that it is
496 * appropriately sized for a module.
497 */
498 *suffix = '\0';
499 const size_t name_sz = strnlen(de->d_name, MODMAXNAMELEN);
500 if (name_sz == 0 || name_sz >= MODMAXNAMELEN) {
501 continue;
502 }
503
504 /* Execute on valid candidate */
505 cb(de->d_name);
506 }
507 return (true);
508 }
509
510 static void
ktest_mod_load_cb(const char * name)511 ktest_mod_load_cb(const char *name)
512 {
513 (void) ktest_mod_load(name);
514 }
515
516 /*
517 * Attempt to load all known test modules.
518 *
519 * Returns true if the modules could be found in their expected directory, and
520 * all were loaded successfully, otherwise false (setting errno). In the case
521 * of error, some modules may have been loaded.
522 */
523 bool
ktest_mod_load_all(void)524 ktest_mod_load_all(void)
525 {
526 return (ktest_mod_for_each(ktest_mod_load_cb));
527 }
528
529 static void
ktest_mod_unload_cb(const char * name)530 ktest_mod_unload_cb(const char *name)
531 {
532 ktest_mod_unload(name);
533 }
534
535 /*
536 * Attempt to unload all known test modules.
537 *
538 * Returns true if the test modules could be iterated over, and unload
539 * operations attempted. A false result (with its accompanying errno) indicates
540 * a problem reading the directory in which the tests reside.
541 */
542 bool
ktest_mod_unload_all(void)543 ktest_mod_unload_all(void)
544 {
545 return (ktest_mod_for_each(ktest_mod_unload_cb));
546 }
547
548 /*
549 * Query the max input data size (in bytes) which can be provided to a test.
550 */
551 size_t
ktest_max_input_size(void)552 ktest_max_input_size(void)
553 {
554 return (KTEST_IOCTL_MAX_LEN);
555 }
556