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