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 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 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 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 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 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 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 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 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 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 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 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 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 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