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