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 2019 Joyent, Inc.
14  */
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <errno.h>
18 #include <fcntl.h>
19 #include <libnvpair.h>
20 #include <string.h>
21 #include <stropts.h>
22 #include <unistd.h>
23 #include <sys/debug.h>
24 #include <sys/ddi_ufm.h>
25 #include <sys/types.h>
26 #include <sys/varargs.h>
27 
28 #include "ufmtest.h"
29 
30 #define	ERRNO_ANY	-1
31 #define	UFMTEST_DEV	"/pseudo/ufmtest@0"
32 
33 static const char *pname;
34 
35 struct ufm_test_state {
36 	uint_t		ufts_n_run;
37 	uint_t		ufts_n_passes;
38 	uint_t		ufts_n_fails;
39 	int		ufts_ufm_fd;
40 	int		ufts_ufmtest_fd;
41 };
42 
43 #define	MAX_IMAGES	5
44 #define	MAX_SLOTS	5
45 #define	MAX_STR		128
46 struct ufm_test_slot_data {
47 	const char			us_vers[MAX_STR];
48 	int				us_attrs;
49 	int				us_nmisc;
50 };
51 
52 struct ufm_test_img_data {
53 	const char			ui_desc[MAX_STR];
54 	int				ui_nslots;
55 	int				ui_nmisc;
56 	struct ufm_test_slot_data	ui_slots[MAX_SLOTS];
57 };
58 
59 struct ufm_test_data {
60 	uint_t				ud_nimages;
61 	struct ufm_test_img_data	ud_images[MAX_IMAGES];
62 };
63 
64 #define	NO_SLOT	{"", -1, -1}
65 #define	NO_IMG	{"", -1, -1, {NO_SLOT, NO_SLOT, NO_SLOT, NO_SLOT, NO_SLOT}}
66 
67 /*
68  * 3 images w\
69  * - 1 slot
70  * - 2 slots (1st active)
71  * - 3 slots (1st active, 3rd empty)
72  */
73 const struct ufm_test_data fw_data1 = {
74 	3,
75 	{
76 	{"fw image 1", 1, 0, {
77 	{"1.0", 4, 0 }, NO_SLOT, NO_SLOT, NO_SLOT, NO_SLOT }},
78 	{"fw image 2", 2, 0, {
79 	{"1.0", 4, 0 }, {"1.1", 0, 0}, NO_SLOT, NO_SLOT, NO_SLOT }},
80 	{"fw image 3", 3, 0, {
81 	{"1.0", 4, 0 }, {"1.1", 0, 0}, {"", 8, 0}, NO_SLOT, NO_SLOT }},
82 	NO_IMG,
83 	NO_IMG
84 	}
85 };
86 
87 /*
88  * Generate an ISO 8601 timestamp
89  */
90 static void
get_timestamp(char * buf,size_t bufsize)91 get_timestamp(char *buf, size_t bufsize)
92 {
93 	time_t utc_time;
94 	struct tm *p_tm;
95 
96 	(void) time(&utc_time);
97 	p_tm = localtime(&utc_time);
98 
99 	(void) strftime(buf, bufsize, "%FT%TZ", p_tm);
100 }
101 
102 /* PRINTFLIKE1 */
103 static void
logmsg(const char * format,...)104 logmsg(const char *format, ...)
105 {
106 	char timestamp[128];
107 	va_list ap;
108 
109 	get_timestamp(timestamp, sizeof (timestamp));
110 	(void) fprintf(stdout, "%s ", timestamp);
111 	va_start(ap, format);
112 	(void) vfprintf(stdout, format, ap);
113 	va_end(ap);
114 	(void) fprintf(stdout, "\n");
115 	(void) fflush(stdout);
116 }
117 
118 static int
do_test_setup(struct ufm_test_state * tst_state)119 do_test_setup(struct ufm_test_state *tst_state)
120 {
121 	if ((tst_state->ufts_ufm_fd = open(DDI_UFM_DEV, O_RDONLY)) < 0) {
122 		logmsg("failed to open %s (%s)", DDI_UFM_DEV,
123 		    strerror(errno));
124 		return (-1);
125 	}
126 	if ((tst_state->ufts_ufmtest_fd = open("/dev/ufmtest", O_RDONLY)) < 0) {
127 		logmsg("failed to open /dev/ufmtest (%s)",
128 		    strerror(errno));
129 		return (0);
130 	}
131 	return (0);
132 }
133 
134 static void
free_nvlist_arr(nvlist_t ** nvlarr,uint_t nelems)135 free_nvlist_arr(nvlist_t **nvlarr, uint_t nelems)
136 {
137 	for (uint_t i = 0; i < nelems; i++) {
138 		if (nvlarr[i] != NULL)
139 			nvlist_free(nvlarr[i]);
140 	}
141 	free(nvlarr);
142 }
143 
144 static int
do_setfw(struct ufm_test_state * tst_state,const struct ufm_test_data * fwdata)145 do_setfw(struct ufm_test_state *tst_state, const struct ufm_test_data *fwdata)
146 {
147 	ufmtest_ioc_setfw_t ioc = { 0 };
148 	nvlist_t *nvl = NULL, **images = NULL, **slots = NULL;
149 	int ret = -1;
150 
151 	if ((images = calloc(sizeof (nvlist_t *), fwdata->ud_nimages)) == NULL)
152 		return (-1);
153 
154 	for (uint_t i = 0; i < fwdata->ud_nimages; i++) {
155 		if (nvlist_alloc(&images[i], NV_UNIQUE_NAME, 0) != 0 ||
156 		    nvlist_add_string(images[i], DDI_UFM_NV_IMAGE_DESC,
157 		    fwdata->ud_images[i].ui_desc) != 0) {
158 			goto out;
159 		}
160 		if ((slots = calloc(sizeof (nvlist_t *),
161 		    fwdata->ud_images[i].ui_nslots)) == NULL) {
162 			goto out;
163 		}
164 
165 		for (int s = 0; s < fwdata->ud_images[i].ui_nslots; s++) {
166 			if (nvlist_alloc(&slots[s], NV_UNIQUE_NAME, 0) != 0 ||
167 			    nvlist_add_string(slots[s], DDI_UFM_NV_SLOT_VERSION,
168 			    fwdata->ud_images[i].ui_slots[s].us_vers) != 0 ||
169 			    nvlist_add_uint32(slots[s], DDI_UFM_NV_SLOT_ATTR,
170 			    fwdata->ud_images[i].ui_slots[s].us_attrs) != 0) {
171 
172 				free_nvlist_arr(slots,
173 				    fwdata->ud_images[i].ui_nslots);
174 				goto out;
175 			}
176 		}
177 
178 		if (nvlist_add_nvlist_array(images[i], DDI_UFM_NV_IMAGE_SLOTS,
179 		    slots, fwdata->ud_images[i].ui_nslots) != 0) {
180 			free_nvlist_arr(slots, fwdata->ud_images[i].ui_nslots);
181 			goto out;
182 		}
183 		free_nvlist_arr(slots, fwdata->ud_images[i].ui_nslots);
184 	}
185 	if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0 ||
186 	    nvlist_add_nvlist_array(nvl, DDI_UFM_NV_IMAGES, images,
187 	    fwdata->ud_nimages) != 0) {
188 		goto out;
189 	}
190 
191 	if (nvlist_size(nvl, &ioc.utsw_bufsz, NV_ENCODE_NATIVE) != 0 ||
192 	    (ioc.utsw_buf = malloc(ioc.utsw_bufsz)) == NULL ||
193 	    nvlist_pack(nvl, &ioc.utsw_buf, &ioc.utsw_bufsz, NV_ENCODE_NATIVE,
194 	    0) != 0) {
195 		goto out;
196 	}
197 
198 	if (ioctl(tst_state->ufts_ufmtest_fd, UFMTEST_IOC_SET_FW, &ioc) < 0) {
199 		logmsg("UFMTEST_IOC_SET_FW ioctl failed (%s)",
200 		    strerror(errno));
201 		return (-1);
202 	}
203 	ret = 0;
204 out:
205 	free_nvlist_arr(images, fwdata->ud_nimages);
206 	nvlist_free(nvl);
207 	free(ioc.utsw_buf);
208 
209 	return (ret);
210 }
211 
212 static int
do_toggle_fails(struct ufm_test_state * tst_state,uint32_t fail_flags)213 do_toggle_fails(struct ufm_test_state *tst_state, uint32_t fail_flags)
214 {
215 	ufmtest_ioc_fails_t ioc = { 0 };
216 
217 	ioc.utfa_flags = fail_flags;
218 
219 	if (ioctl(tst_state->ufts_ufmtest_fd, UFMTEST_IOC_TOGGLE_FAILS,
220 	    &ioc) < 0) {
221 		logmsg("UFMTEST_IOC_TOGGLE_FAILS ioctl failed (%s)",
222 		    strerror(errno));
223 		return (1);
224 	}
225 	return (0);
226 }
227 
228 static int
do_update(struct ufm_test_state * tst_state)229 do_update(struct ufm_test_state *tst_state)
230 {
231 	if (ioctl(tst_state->ufts_ufmtest_fd, UFMTEST_IOC_DO_UPDATE,
232 	    NULL) < 0) {
233 		logmsg("UFMTEST_IOC_DO_UPDATE ioctl failed (%s)",
234 		    strerror(errno));
235 		return (1);
236 	}
237 	return (0);
238 }
239 
240 static int
try_open(int oflag,int exp_errno)241 try_open(int oflag, int exp_errno)
242 {
243 	int fd;
244 
245 	fd = open(DDI_UFM_DEV, oflag);
246 	if (fd != -1) {
247 		logmsg("FAIL: expected open(2) to return -1");
248 		(void) close(fd);
249 		return (-1);
250 	}
251 	if (errno != exp_errno) {
252 		logmsg("FAIL: expected errno to be set to %u (%s)\n"
253 		    "actual errno was %u (%s)", exp_errno, strerror(exp_errno),
254 		    errno, strerror(errno));
255 		return (-1);
256 	}
257 	return (0);
258 }
259 
260 static void
do_negative_open_tests(struct ufm_test_state * tst_state)261 do_negative_open_tests(struct ufm_test_state *tst_state)
262 {
263 	/*
264 	 * Assertion: Opening /dev/ufm in write-only mode will fail with errno
265 	 * set to EINVAL;
266 	 */
267 	logmsg("TEST ufm_open_negative_001: Open %s in write-only mode",
268 	    DDI_UFM_DEV);
269 	if (try_open(O_WRONLY, EINVAL) != 0)
270 		tst_state->ufts_n_fails++;
271 	else
272 		tst_state->ufts_n_passes++;
273 
274 	tst_state->ufts_n_run++;
275 
276 	/*
277 	 * Assertion: Opening /dev/ufm in read-write mode will fail with errno
278 	 * set to EINVAL;
279 	 */
280 	logmsg("TEST ufm_open_negative_002: Open %s in read-write mode",
281 	    DDI_UFM_DEV);
282 	if (try_open(O_RDWR, EINVAL) != 0)
283 		tst_state->ufts_n_fails++;
284 	else
285 		tst_state->ufts_n_passes++;
286 
287 	tst_state->ufts_n_run++;
288 
289 	/*
290 	 * Assertion: Opening /dev/ufm in exclusive mode will fail with errno
291 	 * set to EINVAL;
292 	 */
293 	logmsg("TEST ufm_open_negative_003: Open %s in exclusive mode",
294 	    DDI_UFM_DEV);
295 	if (try_open(O_RDONLY | O_EXCL, EINVAL) != 0)
296 		tst_state->ufts_n_fails++;
297 	else
298 		tst_state->ufts_n_passes++;
299 
300 	tst_state->ufts_n_run++;
301 
302 	/*
303 	 * Assertion: Opening /dev/ufm in non-blocking mode will fail with errno
304 	 * set to EINVAL;
305 	 */
306 	logmsg("TEST ufm_open_negative_004: Open %s in non-block mode",
307 	    DDI_UFM_DEV);
308 	if (try_open(O_RDONLY | O_NONBLOCK, EINVAL) != 0)
309 		tst_state->ufts_n_fails++;
310 	else
311 		tst_state->ufts_n_passes++;
312 
313 	tst_state->ufts_n_run++;
314 
315 	/*
316 	 * Assertion: Opening /dev/ufm in no-delay mode will fail with errno
317 	 * set to EINVAL;
318 	 */
319 	logmsg("TEST ufm_open_negative_005: Open %s in ndelay mode",
320 	    DDI_UFM_DEV);
321 	if (try_open(O_RDONLY | O_NDELAY, EINVAL) != 0)
322 		tst_state->ufts_n_fails++;
323 	else
324 		tst_state->ufts_n_passes++;
325 
326 	tst_state->ufts_n_run++;
327 }
328 
329 
330 static int
try_ioctl(int fd,int cmd,void * arg,int exp_errno)331 try_ioctl(int fd, int cmd, void *arg, int exp_errno)
332 {
333 	int ret;
334 
335 	ret = ioctl(fd, cmd, arg);
336 	if (ret != -1) {
337 		logmsg("FAIL: expected ioctl(2) to return -1");
338 		(void) close(fd);
339 		return (-1);
340 	}
341 	if (exp_errno != ERRNO_ANY && errno != exp_errno) {
342 		logmsg("FAIL: expected errno to be set to %u (%s)\n"
343 		    "actual errno was %u (%s)", exp_errno, strerror(exp_errno),
344 		    errno, strerror(errno));
345 		return (-1);
346 	}
347 	return (0);
348 }
349 
350 /*
351  * These are a set of negative test cases to verify the correctness and
352  * robustness of the DDI UFM ioctl interface.
353  */
354 static void
do_negative_ioctl_tests(struct ufm_test_state * tst_state)355 do_negative_ioctl_tests(struct ufm_test_state *tst_state)
356 {
357 	ufm_ioc_getcaps_t ugc = { 0 };
358 	ufm_ioc_bufsz_t ubz = { 0 };
359 	ufm_ioc_report_t urep = { 0 };
360 	size_t reportsz;
361 	char *buf;
362 	uint_t i, j;
363 
364 	uint8_t not_ascii[MAXPATHLEN];
365 	char no_nul[MAXPATHLEN];
366 
367 	for (uint_t i = 0; i < MAXPATHLEN; i++)
368 		no_nul[i] = '%';
369 
370 	CTASSERT(MAXPATHLEN > 129);
371 	for (i = 0, j = 128; j <= 256; i++, j++)
372 		not_ascii[i] = j;
373 
374 	not_ascii[i] = '\0';
375 
376 	/*
377 	 * Seed the test driver with a set of valid firmware data
378 	 */
379 	if (do_setfw(tst_state, &fw_data1) != 0) {
380 		logmsg("Failed to seed ufmtest driver with fw data");
381 		return;
382 	}
383 
384 	/*
385 	 * Cache the report size, and create a buffer of that size,
386 	 * as we'll need them for some of the tests that follow.
387 	 */
388 	ubz.ufbz_version = DDI_UFM_CURRENT_VERSION;
389 	(void) strlcpy(ubz.ufbz_devpath, UFMTEST_DEV, MAXPATHLEN);
390 	if (ioctl(tst_state->ufts_ufm_fd, UFM_IOC_REPORTSZ, &ubz) < 0) {
391 		logmsg("Failed to get fw data report size");
392 		return;
393 	}
394 	reportsz = ubz.ufbz_size;
395 	if ((buf = malloc(reportsz)) == NULL) {
396 		logmsg("Failed to allocate %u bytes to hold report");
397 		return;
398 	}
399 
400 	/*
401 	 * Assertion: Specifying a DDI UFM version that is out of range in the
402 	 * argument to UFM_IOC_GETCAPS will fail and set errno to ENOTSUP.
403 	 */
404 	logmsg("TEST ufm_getcaps_negative_001: Bad DDI UFM version (too low)");
405 	ugc.ufmg_version = 0;
406 	(void) strlcpy(ugc.ufmg_devpath, UFMTEST_DEV, MAXPATHLEN);
407 	if (try_ioctl(tst_state->ufts_ufm_fd, UFM_IOC_GETCAPS, &ugc,
408 	    ENOTSUP) != 0)
409 		tst_state->ufts_n_fails++;
410 	else
411 		tst_state->ufts_n_passes++;
412 
413 	tst_state->ufts_n_run++;
414 
415 	logmsg("TEST ufm_getcaps_negative_002: Bad DDI UFM version (too high)");
416 	ugc.ufmg_version = 999;
417 	(void) strlcpy(ugc.ufmg_devpath, UFMTEST_DEV, MAXPATHLEN);
418 	if (try_ioctl(tst_state->ufts_ufm_fd, UFM_IOC_GETCAPS, &ugc,
419 	    ENOTSUP) != 0)
420 		tst_state->ufts_n_fails++;
421 	else
422 		tst_state->ufts_n_passes++;
423 
424 	tst_state->ufts_n_run++;
425 
426 	/*
427 	 * Assertion: Specifying a bad device pathname in the argument to
428 	 * UFM_IOC_GETCAPS will cause the ioctl to fail, but the driver will
429 	 * not hang or panic.
430 	 */
431 	logmsg("TEST ufm_getcaps_negative_003: Bad devpath (empty)");
432 	ugc.ufmg_version = DDI_UFM_CURRENT_VERSION;
433 	ugc.ufmg_devpath[0] = '\0';
434 	if (try_ioctl(tst_state->ufts_ufm_fd, UFM_IOC_GETCAPS, &ugc,
435 	    ERRNO_ANY) != 0)
436 		tst_state->ufts_n_fails++;
437 	else
438 		tst_state->ufts_n_passes++;
439 
440 	tst_state->ufts_n_run++;
441 
442 	logmsg("TEST ufm_getcaps_negative_004: Bad devpath (not a device)");
443 	(void) strlcpy(ugc.ufmg_devpath, "/usr/bin/ls", MAXPATHLEN);
444 	if (try_ioctl(tst_state->ufts_ufm_fd, UFM_IOC_GETCAPS, &ugc,
445 	    ERRNO_ANY) != 0)
446 		tst_state->ufts_n_fails++;
447 	else
448 		tst_state->ufts_n_passes++;
449 
450 	tst_state->ufts_n_run++;
451 
452 	logmsg("TEST ufm_getcaps_negative_005: Bad devpath (not UFM device)");
453 	(void) strlcpy(ugc.ufmg_devpath, "/dev/stdout", MAXPATHLEN);
454 	if (try_ioctl(tst_state->ufts_ufm_fd, UFM_IOC_GETCAPS, &ugc,
455 	    ERRNO_ANY) != 0)
456 		tst_state->ufts_n_fails++;
457 	else
458 		tst_state->ufts_n_passes++;
459 
460 	tst_state->ufts_n_run++;
461 
462 	logmsg("TEST ufm_getcaps_negative_006: Bad devpath (no NUL term)");
463 	(void) strncpy(ugc.ufmg_devpath, no_nul, MAXPATHLEN);
464 	if (try_ioctl(tst_state->ufts_ufm_fd, UFM_IOC_GETCAPS, &ugc,
465 	    ERRNO_ANY) != 0)
466 		tst_state->ufts_n_fails++;
467 	else
468 		tst_state->ufts_n_passes++;
469 
470 	tst_state->ufts_n_run++;
471 
472 	logmsg("TEST ufm_getcaps_negative_007: Bad devpath (not ascii str)");
473 	(void) strlcpy(ugc.ufmg_devpath, (char *)not_ascii, MAXPATHLEN);
474 	if (try_ioctl(tst_state->ufts_ufm_fd, UFM_IOC_GETCAPS, &ugc,
475 	    ERRNO_ANY) != 0)
476 		tst_state->ufts_n_fails++;
477 	else
478 		tst_state->ufts_n_passes++;
479 
480 	tst_state->ufts_n_run++;
481 
482 	/*
483 	 * Assertion: Specifying a DDI UFM version that is out of range in the
484 	 * argument to UFM_IOC_REPORTSZ will fail and set errno to ENOTSUP.
485 	 */
486 	logmsg("TEST ufm_reportsz_negative_001: Bad DDI UFM version (too low)");
487 	ubz.ufbz_version = 0;
488 	(void) strlcpy(ubz.ufbz_devpath, UFMTEST_DEV, MAXPATHLEN);
489 	if (try_ioctl(tst_state->ufts_ufm_fd, UFM_IOC_REPORTSZ, &ubz,
490 	    ENOTSUP) != 0)
491 		tst_state->ufts_n_fails++;
492 	else
493 		tst_state->ufts_n_passes++;
494 
495 	tst_state->ufts_n_run++;
496 
497 	logmsg("TEST ufm_reportsz_negative_002: Bad DDI UFM version (too "
498 	    "high)");
499 	ubz.ufbz_version = 999;
500 	(void) strlcpy(ubz.ufbz_devpath, UFMTEST_DEV, MAXPATHLEN);
501 	if (try_ioctl(tst_state->ufts_ufm_fd, UFM_IOC_REPORTSZ, &ubz,
502 	    ENOTSUP) != 0)
503 		tst_state->ufts_n_fails++;
504 	else
505 		tst_state->ufts_n_passes++;
506 
507 	tst_state->ufts_n_run++;
508 
509 	/*
510 	 * Assertion: Specifying a bad device pathname in the argument to
511 	 * UFM_IOC_REPORTSZ will cause the ioctl to fail, but the driver will
512 	 * not hang or panic.
513 	 */
514 	logmsg("TEST ufm_reportsz_negative_003: Bad devpath (empty)");
515 	ubz.ufbz_version = DDI_UFM_CURRENT_VERSION;
516 	ubz.ufbz_devpath[0] = '\0';
517 	if (try_ioctl(tst_state->ufts_ufm_fd, UFM_IOC_REPORTSZ, &ubz,
518 	    ERRNO_ANY) != 0)
519 		tst_state->ufts_n_fails++;
520 	else
521 		tst_state->ufts_n_passes++;
522 
523 	tst_state->ufts_n_run++;
524 
525 	logmsg("TEST ufm_reportsz_negative_004: Bad devpath (not a device)");
526 	ubz.ufbz_version = DDI_UFM_CURRENT_VERSION;
527 	(void) strlcpy(ubz.ufbz_devpath, "/usr/bin/ls", MAXPATHLEN);
528 	if (try_ioctl(tst_state->ufts_ufm_fd, UFM_IOC_REPORTSZ, &ubz,
529 	    ERRNO_ANY) != 0)
530 		tst_state->ufts_n_fails++;
531 	else
532 		tst_state->ufts_n_passes++;
533 
534 	tst_state->ufts_n_run++;
535 
536 	logmsg("TEST ufm_reportsz_negative_005: Bad devpath (not UFM device)");
537 	ubz.ufbz_version = DDI_UFM_CURRENT_VERSION;
538 	(void) strlcpy(ubz.ufbz_devpath, "/dev/stdout", MAXPATHLEN);
539 	if (try_ioctl(tst_state->ufts_ufm_fd, UFM_IOC_REPORTSZ, &ubz,
540 	    ERRNO_ANY) != 0)
541 		tst_state->ufts_n_fails++;
542 	else
543 		tst_state->ufts_n_passes++;
544 
545 	tst_state->ufts_n_run++;
546 
547 	logmsg("TEST ufm_reportsz_negative_006: Bad devpath (no NUL term)");
548 	ubz.ufbz_version = DDI_UFM_CURRENT_VERSION;
549 	(void) strncpy(ubz.ufbz_devpath, no_nul, MAXPATHLEN);
550 	if (try_ioctl(tst_state->ufts_ufm_fd, UFM_IOC_REPORTSZ, &ubz,
551 	    ERRNO_ANY) != 0)
552 		tst_state->ufts_n_fails++;
553 	else
554 		tst_state->ufts_n_passes++;
555 
556 	tst_state->ufts_n_run++;
557 
558 	logmsg("TEST ufm_reportsz_negative_007: Bad devpath (not ascii str)");
559 	ubz.ufbz_version = DDI_UFM_CURRENT_VERSION;
560 	(void) strlcpy(ubz.ufbz_devpath, (char *)not_ascii, MAXPATHLEN);
561 	if (try_ioctl(tst_state->ufts_ufm_fd, UFM_IOC_REPORTSZ, &ubz,
562 	    ERRNO_ANY) != 0)
563 		tst_state->ufts_n_fails++;
564 	else
565 		tst_state->ufts_n_passes++;
566 
567 	tst_state->ufts_n_run++;
568 
569 	/*
570 	 * Assertion: Specifying a DDI UFM version that is out of range in the
571 	 * argument to UFM_IOC_REPORT will fail and set errno to ENOTSUP.
572 	 */
573 	logmsg("TEST ufm_report_negative_001: Bad DDI UFM version (too low)");
574 	urep.ufmr_version = 0;
575 	urep.ufmr_bufsz = reportsz;
576 	urep.ufmr_buf = buf;
577 	(void) strlcpy(urep.ufmr_devpath, UFMTEST_DEV, MAXPATHLEN);
578 	if (try_ioctl(tst_state->ufts_ufm_fd, UFM_IOC_REPORT, &urep,
579 	    ENOTSUP) != 0)
580 		tst_state->ufts_n_fails++;
581 	else
582 		tst_state->ufts_n_passes++;
583 
584 	tst_state->ufts_n_run++;
585 
586 	logmsg("TEST ufm_report_negative_002: Bad DDI UFM version (too high)");
587 	urep.ufmr_version = 999;
588 	urep.ufmr_bufsz = reportsz;
589 	urep.ufmr_buf = buf;
590 	(void) strlcpy(urep.ufmr_devpath, UFMTEST_DEV, MAXPATHLEN);
591 	if (try_ioctl(tst_state->ufts_ufm_fd, UFM_IOC_REPORT, &urep,
592 	    ENOTSUP) != 0)
593 		tst_state->ufts_n_fails++;
594 	else
595 		tst_state->ufts_n_passes++;
596 
597 	tst_state->ufts_n_run++;
598 
599 	/*
600 	 * Assertion: Specifying a bad device pathname in the argument to
601 	 * UFM_IOC_REPORT will cause the ioctl to fail, but the driver will
602 	 * not hang or panic.
603 	 */
604 	logmsg("TEST ufm_report_negative_003: Bad devpath (empty)");
605 	urep.ufmr_version = DDI_UFM_CURRENT_VERSION;
606 	urep.ufmr_bufsz = reportsz;
607 	urep.ufmr_buf = buf;
608 	urep.ufmr_devpath[0] = '\0';
609 	if (try_ioctl(tst_state->ufts_ufm_fd, UFM_IOC_REPORT, &urep,
610 	    ERRNO_ANY) != 0)
611 		tst_state->ufts_n_fails++;
612 	else
613 		tst_state->ufts_n_passes++;
614 
615 	tst_state->ufts_n_run++;
616 
617 	logmsg("TEST ufm_report_negative_004: Bad devpath (not a device)");
618 	urep.ufmr_version = DDI_UFM_CURRENT_VERSION;
619 	urep.ufmr_bufsz = reportsz;
620 	urep.ufmr_buf = buf;
621 	(void) strlcpy(urep.ufmr_devpath, "/usr/bin/ls", MAXPATHLEN);
622 	if (try_ioctl(tst_state->ufts_ufm_fd, UFM_IOC_REPORT, &urep,
623 	    ERRNO_ANY) != 0)
624 		tst_state->ufts_n_fails++;
625 	else
626 		tst_state->ufts_n_passes++;
627 
628 	tst_state->ufts_n_run++;
629 
630 	logmsg("TEST ufm_report_negative_005: Bad devpath (not UFM device)");
631 	urep.ufmr_version = DDI_UFM_CURRENT_VERSION;
632 	urep.ufmr_bufsz = reportsz;
633 	urep.ufmr_buf = buf;
634 	(void) strlcpy(urep.ufmr_devpath, "/dev/stdout", MAXPATHLEN);
635 	if (try_ioctl(tst_state->ufts_ufm_fd, UFM_IOC_REPORT, &urep,
636 	    ERRNO_ANY) != 0)
637 		tst_state->ufts_n_fails++;
638 	else
639 		tst_state->ufts_n_passes++;
640 
641 	tst_state->ufts_n_run++;
642 
643 	logmsg("TEST ufm_report_negative_006: Bad devpath (no NUL term)");
644 	urep.ufmr_version = DDI_UFM_CURRENT_VERSION;
645 	urep.ufmr_bufsz = reportsz;
646 	urep.ufmr_buf = buf;
647 	(void) strncpy(urep.ufmr_devpath, no_nul, MAXPATHLEN);
648 	if (try_ioctl(tst_state->ufts_ufm_fd, UFM_IOC_REPORT, &urep,
649 	    ERRNO_ANY) != 0)
650 		tst_state->ufts_n_fails++;
651 	else
652 		tst_state->ufts_n_passes++;
653 
654 	tst_state->ufts_n_run++;
655 
656 	logmsg("TEST ufm_report_negative_007: Bad devpath (not ascii str)");
657 	urep.ufmr_version = DDI_UFM_CURRENT_VERSION;
658 	urep.ufmr_bufsz = reportsz;
659 	urep.ufmr_buf = buf;
660 	(void) strlcpy(urep.ufmr_devpath, (char *)not_ascii, MAXPATHLEN);
661 	if (try_ioctl(tst_state->ufts_ufm_fd, UFM_IOC_REPORT, &urep,
662 	    ERRNO_ANY) != 0)
663 		tst_state->ufts_n_fails++;
664 	else
665 		tst_state->ufts_n_passes++;
666 
667 	tst_state->ufts_n_run++;
668 
669 	/*
670 	 * Assertion: Passing a bufsz that is too small to the UFM_IOC_REPORT
671 	 * ioctl will cause the ioctl to fail, but the driver will not hang or
672 	 * panic.
673 	 */
674 	logmsg("TEST ufm_report_negative_008: bad bufsz (too small)");
675 	urep.ufmr_version = DDI_UFM_CURRENT_VERSION;
676 	urep.ufmr_bufsz = 10;
677 	urep.ufmr_buf = buf;
678 	(void) strlcpy(urep.ufmr_devpath, UFMTEST_DEV, MAXPATHLEN);
679 	if (try_ioctl(tst_state->ufts_ufm_fd, UFM_IOC_REPORT, &urep,
680 	    ERRNO_ANY) != 0)
681 		tst_state->ufts_n_fails++;
682 	else
683 		tst_state->ufts_n_passes++;
684 
685 	tst_state->ufts_n_run++;
686 
687 	/*
688 	 * Assertion: Passing a bufsz that is too small to the UFM_IOC_REPORT
689 	 * ioctl will cause the ioctl to fail, but the driver will not hang or
690 	 * panic.
691 	 */
692 	logmsg("TEST ufm_report_negative_009: bad buf (NULL pointer)");
693 	urep.ufmr_version = DDI_UFM_CURRENT_VERSION;
694 	urep.ufmr_bufsz = 10;
695 	urep.ufmr_buf = NULL;
696 	(void) strlcpy(urep.ufmr_devpath, UFMTEST_DEV, MAXPATHLEN);
697 	if (try_ioctl(tst_state->ufts_ufm_fd, UFM_IOC_REPORT, &urep,
698 	    ERRNO_ANY) != 0)
699 		tst_state->ufts_n_fails++;
700 	else
701 		tst_state->ufts_n_passes++;
702 
703 	tst_state->ufts_n_run++;
704 }
705 
706 /*
707  * These are a set of negative test cases to verify the correctness and
708  * robustness of the DDI UFM subsystems when a driver UFM callback returns
709  * an error.
710  *
711  * For each callback, we do the following:
712  *
713  * 1. Toggle the callback failure via a UFMTEST_IOC_TOGGLE_FAILS ioctl
714  * 2. Force a ddi_ufm_update() via a UFMTEST_IOC_DO_UPDATE ioctl.  This is
715  *    done in order to invalidate any cached firmware data for this device.
716  * 3. Call UFM_IOC_REPORTSZ ioctl to force the ufm_cache_fill() codepath to
717  *    be executed.
718  */
719 static void
do_negative_callback_tests(struct ufm_test_state * tst_state)720 do_negative_callback_tests(struct ufm_test_state *tst_state)
721 {
722 	ufm_ioc_getcaps_t ugc = { 0 };
723 	ufm_ioc_bufsz_t ubz = { 0 };
724 	uint32_t failflags;
725 	boolean_t failed;
726 
727 	/*
728 	 * Seed the test driver with a set of valid firmware data
729 	 */
730 	if (do_setfw(tst_state, &fw_data1) != 0) {
731 		logmsg("Failed to seed ufmtest driver with fw data");
732 		return;
733 	}
734 
735 	/*
736 	 * Assertion: If a driver's ddi_ufm_op_getcaps callback returns a
737 	 * failure, the kernel should not hang or panic when servicing a
738 	 * UFM_IOC_REPORTSZ ioctl.  Furthermore, the UFM_IOC_REPORTSZ ioctl
739 	 * should fail.
740 	 */
741 	logmsg("TEST ufm_callback_negative_001: ddi_ufm_op_getcaps fails");
742 	failed = B_FALSE;
743 	failflags = UFMTEST_FAIL_GETCAPS;
744 	if (do_toggle_fails(tst_state, failflags) != 0 ||
745 	    do_update(tst_state) != 0) {
746 		failed = B_TRUE;
747 	}
748 
749 	ubz.ufbz_version = DDI_UFM_CURRENT_VERSION;
750 	(void) strlcpy(ubz.ufbz_devpath, UFMTEST_DEV, MAXPATHLEN);
751 	if (try_ioctl(tst_state->ufts_ufm_fd, UFM_IOC_REPORTSZ, &ubz,
752 	    ERRNO_ANY) != 0)
753 		failed = B_TRUE;
754 
755 	if (failed)
756 		tst_state->ufts_n_fails++;
757 	else
758 		tst_state->ufts_n_passes++;
759 
760 	tst_state->ufts_n_run++;
761 
762 	/*
763 	 * Assertion: If a driver's ddi_ufm_op_getcaps callback returns a
764 	 * failure, the kernel should not hang or panic when servicing a
765 	 * UFM_IOC_GETCAPS ioctl for that device.  Furthermore, the
766 	 * UFM_IOC_GETCAPS ioctl should fail.
767 	 */
768 	logmsg("TEST ufm_callback_negative_002: ddi_ufm_op_getcaps fails");
769 	failed = B_FALSE;
770 	ugc.ufmg_version = DDI_UFM_CURRENT_VERSION;
771 	(void) strlcpy(ugc.ufmg_devpath, UFMTEST_DEV, MAXPATHLEN);
772 	if (try_ioctl(tst_state->ufts_ufm_fd, UFM_IOC_GETCAPS, &ugc,
773 	    ERRNO_ANY) != 0)
774 		tst_state->ufts_n_fails++;
775 	else
776 		tst_state->ufts_n_passes++;
777 
778 	tst_state->ufts_n_run++;
779 
780 	/*
781 	 * Assertion: If a driver's ddi_ufm_op_nimages callback returns a
782 	 * failure, the kernel should not hang or panic when servicing a
783 	 * UFM_IOC_REPORTSZ ioctl.  Furthermore, the UFM_IOC_REPORTSZ ioctl
784 	 * should fail.
785 	 */
786 	logmsg("TEST ufm_callback_negative_003: ddi_ufm_op_nimages fails");
787 	failed = B_FALSE;
788 	failflags = UFMTEST_FAIL_NIMAGES;
789 	if (do_toggle_fails(tst_state, failflags) != 0 ||
790 	    do_update(tst_state) != 0) {
791 		failed = B_TRUE;
792 	}
793 
794 	ubz.ufbz_version = DDI_UFM_CURRENT_VERSION;
795 	(void) strlcpy(ubz.ufbz_devpath, UFMTEST_DEV, MAXPATHLEN);
796 	if (try_ioctl(tst_state->ufts_ufm_fd, UFM_IOC_REPORTSZ, &ubz,
797 	    ERRNO_ANY) != 0)
798 		failed = B_TRUE;
799 
800 	if (failed)
801 		tst_state->ufts_n_fails++;
802 	else
803 		tst_state->ufts_n_passes++;
804 
805 	tst_state->ufts_n_run++;
806 
807 	/*
808 	 * Assertion: If a driver's ddi_ufm_op_fill_image callback returns a
809 	 * failure, the kernel should not hang or panic when servicing a
810 	 * UFM_IOC_REPORTSZ ioctl.  Furthermore, the UFM_IOC_REPORTSZ ioctl
811 	 * should fail.
812 	 */
813 	logmsg("TEST ufm_callback_negative_004: ddi_ufm_op_fill_image fails");
814 	failed = B_FALSE;
815 	failflags = UFMTEST_FAIL_FILLIMAGE;
816 	if (do_toggle_fails(tst_state, failflags) != 0 ||
817 	    do_update(tst_state) != 0) {
818 		failed = B_TRUE;
819 	}
820 
821 	ubz.ufbz_version = DDI_UFM_CURRENT_VERSION;
822 	(void) strlcpy(ubz.ufbz_devpath, UFMTEST_DEV, MAXPATHLEN);
823 	if (try_ioctl(tst_state->ufts_ufm_fd, UFM_IOC_REPORTSZ, &ubz,
824 	    ERRNO_ANY) != 0)
825 		failed = B_TRUE;
826 
827 	if (failed)
828 		tst_state->ufts_n_fails++;
829 	else
830 		tst_state->ufts_n_passes++;
831 
832 	tst_state->ufts_n_run++;
833 
834 	/*
835 	 * Assertion: If a driver's ddi_ufm_op_fill_slot callback returns a
836 	 * failure, the kernel should not hang or panic when servicing a
837 	 * UFM_IOC_REPORTSZ ioctl.  Furthermore, the UFM_IOC_REPORTSZ ioctl
838 	 * should fail.
839 	 */
840 	logmsg("TEST ufm_callback_negative_005: ddi_ufm_op_fill_slot fails");
841 	failed = B_FALSE;
842 	failflags = UFMTEST_FAIL_FILLSLOT;
843 	if (do_toggle_fails(tst_state, failflags) != 0 ||
844 	    do_update(tst_state) != 0) {
845 		failed = B_TRUE;
846 	}
847 
848 	ubz.ufbz_version = DDI_UFM_CURRENT_VERSION;
849 	(void) strlcpy(ubz.ufbz_devpath, UFMTEST_DEV, MAXPATHLEN);
850 	if (try_ioctl(tst_state->ufts_ufm_fd, UFM_IOC_REPORTSZ, &ubz,
851 	    ERRNO_ANY) != 0)
852 		failed = B_TRUE;
853 
854 	if (failed)
855 		tst_state->ufts_n_fails++;
856 	else
857 		tst_state->ufts_n_passes++;
858 
859 	tst_state->ufts_n_run++;
860 
861 	/* Unset the fail flags */
862 	failflags = 0;
863 	if (do_toggle_fails(tst_state, failflags) != 0)
864 		logmsg("Failed to clear fail flags");
865 }
866 
867 int
main(int argc,char ** argv)868 main(int argc, char **argv)
869 {
870 	int status = EXIT_FAILURE;
871 	struct ufm_test_state tst_state = { 0 };
872 
873 	pname = argv[0];
874 
875 	if (do_test_setup(&tst_state) != 0) {
876 		logmsg("Test setup failed - exiting");
877 		return (status);
878 	}
879 
880 	do_negative_open_tests(&tst_state);
881 
882 	if (tst_state.ufts_ufmtest_fd > 0) {
883 		do_negative_ioctl_tests(&tst_state);
884 		do_negative_callback_tests(&tst_state);
885 	}
886 
887 	logmsg("Number of Tests Run: %u", tst_state.ufts_n_run);
888 	logmsg("Number of Passes:    %u", tst_state.ufts_n_passes);
889 	logmsg("Number of Fails :    %u", tst_state.ufts_n_fails);
890 	if (tst_state.ufts_n_fails == 0)
891 		status = EXIT_SUCCESS;
892 
893 	(void) close(tst_state.ufts_ufm_fd);
894 	if (tst_state.ufts_ufmtest_fd >= 0)
895 		(void) close(tst_state.ufts_ufmtest_fd);
896 	return (status);
897 }
898