1 /* 2 * This file and its contents are supplied under the terms of the 3 * Common Development and Distribution License ("CDDL"), version 1.0. 4 * You may only use this file in accordance with the terms of version 5 * 1.0 of the CDDL. 6 * 7 * A full copy of the text of the CDDL should have accompanied this 8 * source. A copy of the CDDL is also available via the Internet at 9 * http://www.illumos.org/license/CDDL. 10 */ 11 12 /* 13 * Copyright 2025 Oxide Computer Company 14 */ 15 16 /* 17 * libnvme pieces specific to WDC. 18 * 19 * Currently this defines several common log pages that are found in a few 20 * generations of WDC devices such as the SN840 and SN65x. There is also support 21 * for a few of the vendor specific commands in the device. 22 * 23 * Currently there is support for two commands in library form: getting an e6 24 * log and performing a device resize. Because there are a few different 25 * parameters needed to issue the e6 request, we end up structuring it like the 26 * library's other request structures, even though it just uses the vendor 27 * unique commands. We do not use the full field validation structures for this 28 * because a portion of that is used by the vendor unique subsystem. Instead we 29 * manually validate the offset and track fields being set. 30 */ 31 32 #include <string.h> 33 #include <sys/sysmacros.h> 34 #include <sys/nvme/wdc.h> 35 36 #include "libnvme_impl.h" 37 38 /* 39 * The amount of time that this command takes appears to somewhat relate to the 40 * size of the overall device and transformations that are going on. This value 41 * is an attempt to get through most resize testing plus a little slack in 42 * all of our testing to date. 43 */ 44 static const uint32_t nvme_wdc_resize_timeout = 30; 45 46 /* 47 * We expect a given read of a region of an e6 log to take this amount of time 48 * in seconds. 49 */ 50 static const uint32_t nvme_wdc_e6_timeout = 30; 51 52 /* 53 * Timeout for injecting and clearing asserts. We make this generous as assert 54 * injection may take some time. 55 */ 56 static const uint32_t nvme_wdc_assert_timeout = 45; 57 58 typedef enum { 59 NVME_WDC_E6_REQ_FIELD_OFFSET = 0, 60 NVME_WDC_E6_REQ_FIELD_LEN 61 } nvme_wdc_e6_req_field_t; 62 63 static bool 64 nvme_wdc_e6_field_valid_offset(const nvme_field_info_t *field, 65 const nvme_valid_ctrl_data_t *data, uint64_t off, char *msg, size_t msglen) 66 { 67 uint64_t max; 68 69 if ((off % NVME_DWORD_SIZE) != 0) { 70 (void) snprintf(msg, msglen, "field %s (%s) value 0x%" PRIx64 71 "must be %u-byte aligned", field->nlfi_human, 72 field->nlfi_spec, off, NVME_DWORD_SIZE); 73 return (false); 74 } 75 76 max = (uint64_t)UINT32_MAX << NVME_DWORD_SHIFT; 77 return (nvme_field_range_check(field, 0, max, msg, msglen, off)); 78 } 79 80 const nvme_field_info_t nvme_wdc_e6_req_fields[] = { 81 [NVME_WDC_E6_REQ_FIELD_OFFSET] = { 82 .nlfi_vers = &nvme_vers_1v0, 83 .nlfi_valid = nvme_wdc_e6_field_valid_offset, 84 .nlfi_spec = "offset", 85 .nlfi_human = "e6 log offset", 86 .nlfi_def_req = true, 87 .nlfi_def_allow = true 88 }, 89 /* 90 * Note there is no validation of this field because we rely on the 91 * underlying vendor unique command output length to do so. 92 */ 93 [NVME_WDC_E6_REQ_FIELD_LEN] = { 94 .nlfi_vers = &nvme_vers_1v0, 95 .nlfi_spec = "length", 96 .nlfi_human = "data transfer length", 97 .nlfi_def_req = true, 98 .nlfi_def_allow = true 99 }, 100 }; 101 102 static bool 103 nvme_wdc_log_dev_mgmt_var_len(uint64_t *outp, const void *data, size_t len) 104 { 105 wdc_vsd_t vsd; 106 107 if (len < sizeof (vsd)) { 108 return (false); 109 } 110 111 (void) memcpy(&vsd, data, sizeof (vsd)); 112 *outp = vsd.vsd_len; 113 return (true); 114 } 115 116 static bool 117 nvme_wdc_log_samples_var_len(uint64_t *outp, const void *data, size_t len) 118 { 119 uint32_t nsamp; 120 121 if (len < sizeof (uint32_t)) { 122 return (false); 123 } 124 125 (void) memcpy(&nsamp, data, sizeof (uint32_t)); 126 *outp = (uint64_t)nsamp * sizeof (uint32_t); 127 return (true); 128 } 129 130 static bool 131 nvme_wdc_sn840_fw_act_var_len(uint64_t *outp, const void *data, size_t len) 132 { 133 wdc_vul_sn840_fw_act_hdr_t hdr; 134 135 if (len < sizeof (wdc_vul_sn840_fw_act_hdr_t)) { 136 return (false); 137 } 138 139 (void) memcpy(&hdr, data, sizeof (uint32_t)); 140 *outp = (uint64_t)hdr.fah_nent * hdr.fah_entlen; 141 return (true); 142 } 143 144 static const nvme_log_page_info_t wdc_sn840_log_eol = { 145 .nlpi_short = "wdc/eol", 146 .nlpi_human = "EOL", 147 .nlpi_lid = WDC_SN840_LOG_EOL, 148 .nlpi_csi = NVME_CSI_NVM, 149 .nlpi_kind = NVME_LOG_ID_VENDOR_SPECIFIC, 150 .nlpi_source = NVME_LOG_DISC_S_DB, 151 .nlpi_scope = NVME_LOG_SCOPE_NVM, 152 .nlpi_len = sizeof (wdc_vul_sn840_eol_t) 153 }; 154 155 static const nvme_log_page_info_t wdc_sn840_log_devmgmt = { 156 .nlpi_short = "wdc/devmgmt", 157 .nlpi_human = "Device Manageability", 158 .nlpi_lid = WDC_SN840_LOG_DEV_MANAGE, 159 .nlpi_csi = NVME_CSI_NVM, 160 .nlpi_kind = NVME_LOG_ID_VENDOR_SPECIFIC, 161 .nlpi_source = NVME_LOG_DISC_S_DB, 162 .nlpi_scope = NVME_LOG_SCOPE_CTRL | NVME_LOG_SCOPE_NS, 163 .nlpi_len = sizeof (wdc_vsd_t), 164 .nlpi_var_func = nvme_wdc_log_dev_mgmt_var_len 165 }; 166 167 static const nvme_log_page_info_t wdc_sn840_log_pciesi = { 168 .nlpi_short = "wdc/pciesi", 169 .nlpi_human = "PCIe Signal Integrity", 170 .nlpi_lid = WDC_SN840_LOG_PCIE_SI, 171 .nlpi_csi = NVME_CSI_NVM, 172 .nlpi_kind = NVME_LOG_ID_VENDOR_SPECIFIC, 173 .nlpi_source = NVME_LOG_DISC_S_DB, 174 .nlpi_disc = NVME_LOG_DISC_F_NEED_LSP, 175 .nlpi_scope = NVME_LOG_SCOPE_CTRL 176 }; 177 178 static const nvme_log_page_info_t wdc_sn840_log_power = { 179 .nlpi_short = "wdc/power", 180 .nlpi_human = "Power Samples", 181 .nlpi_lid = WDC_SN840_LOG_POWER, 182 .nlpi_csi = NVME_CSI_NVM, 183 .nlpi_kind = NVME_LOG_ID_VENDOR_SPECIFIC, 184 .nlpi_source = NVME_LOG_DISC_S_DB, 185 .nlpi_scope = NVME_LOG_SCOPE_CTRL, 186 .nlpi_len = sizeof (uint32_t), 187 .nlpi_var_func = nvme_wdc_log_samples_var_len 188 }; 189 190 static const nvme_log_page_info_t wdc_sn840_log_temp = { 191 .nlpi_short = "wdc/temp", 192 .nlpi_human = "Temperature Samples", 193 .nlpi_lid = WDC_SN840_LOG_TEMP, 194 .nlpi_kind = NVME_LOG_ID_VENDOR_SPECIFIC, 195 .nlpi_source = NVME_LOG_DISC_S_DB, 196 .nlpi_scope = NVME_LOG_SCOPE_CTRL, 197 .nlpi_len = sizeof (uint32_t), 198 .nlpi_var_func = nvme_wdc_log_samples_var_len 199 }; 200 201 static const nvme_log_page_info_t wdc_sn840_log_fwact = { 202 .nlpi_short = "wdc/fwact", 203 .nlpi_human = "Firmware Activation", 204 .nlpi_lid = WDC_SN840_LOG_FW_ACT, 205 .nlpi_kind = NVME_LOG_ID_VENDOR_SPECIFIC, 206 .nlpi_source = NVME_LOG_DISC_S_DB, 207 .nlpi_scope = NVME_LOG_SCOPE_CTRL, 208 .nlpi_len = sizeof (wdc_vul_sn840_fw_act_hdr_t), 209 .nlpi_var_func = nvme_wdc_sn840_fw_act_var_len 210 }; 211 212 static const nvme_log_page_info_t wdc_sn840_log_cdds = { 213 .nlpi_short = "wdc/ccds", 214 .nlpi_human = "CCDS Build Information", 215 .nlpi_lid = WDC_SN840_LOG_CCDS, 216 .nlpi_kind = NVME_LOG_ID_VENDOR_SPECIFIC, 217 .nlpi_source = NVME_LOG_DISC_S_DB, 218 .nlpi_scope = NVME_LOG_SCOPE_CTRL, 219 .nlpi_len = sizeof (wdc_vul_sn840_ccds_info_t) 220 }; 221 222 static const nvme_log_page_info_t *wdc_sn840_log_pages[] = { 223 &wdc_sn840_log_eol, &wdc_sn840_log_devmgmt, &wdc_sn840_log_pciesi, 224 &wdc_sn840_log_power, &wdc_sn840_log_temp, &wdc_sn840_log_fwact, 225 &wdc_sn840_log_cdds 226 }; 227 228 static const nvme_log_page_info_t wdc_sn65x_log_power = { 229 .nlpi_short = "wdc/power", 230 .nlpi_human = "Power Samples", 231 .nlpi_lid = WDC_SN65X_LOG_POWER, 232 .nlpi_csi = NVME_CSI_NVM, 233 .nlpi_kind = NVME_LOG_ID_VENDOR_SPECIFIC, 234 .nlpi_source = NVME_LOG_DISC_S_DB, 235 .nlpi_scope = NVME_LOG_SCOPE_CTRL, 236 .nlpi_len = sizeof (uint32_t), 237 .nlpi_var_func = nvme_wdc_log_samples_var_len 238 }; 239 240 static const nvme_log_page_info_t wdc_sn65x_log_temp = { 241 .nlpi_short = "wdc/temp", 242 .nlpi_human = "Temperature Samples", 243 .nlpi_lid = WDC_SN65X_LOG_TEMP, 244 .nlpi_kind = NVME_LOG_ID_VENDOR_SPECIFIC, 245 .nlpi_source = NVME_LOG_DISC_S_DB, 246 .nlpi_scope = NVME_LOG_SCOPE_CTRL, 247 .nlpi_len = sizeof (uint32_t), 248 .nlpi_var_func = nvme_wdc_log_samples_var_len 249 }; 250 251 static const nvme_log_page_info_t wdc_sn65x_log_cusmart = { 252 .nlpi_short = "wdc/cusmart", 253 .nlpi_human = "Customer Unique SMART", 254 .nlpi_lid = WDC_SN65X_LOG_UNIQUE_SMART, 255 .nlpi_kind = NVME_LOG_ID_VENDOR_SPECIFIC, 256 .nlpi_source = NVME_LOG_DISC_S_DB, 257 .nlpi_scope = NVME_LOG_SCOPE_CTRL, 258 .nlpi_len = sizeof (wdc_vul_sn65x_smart_t) 259 }; 260 261 static const nvme_log_page_info_t *wdc_sn65x_log_pages[] = { 262 &ocp_log_smart, &wdc_sn65x_log_power, &wdc_sn65x_log_temp, 263 &wdc_sn65x_log_cusmart 264 }; 265 266 267 /* 268 * Currently these commands are shared across the SN840, SN650, and SN655. 269 * This will likely need to be split up and redone when we end up with more 270 * device-specific commands that aren't shared across controller generations. 271 * When we get to that we should choose whether we want to redefine the vuc like 272 * we have with log pages or if we should move to a shared structure that is 273 * incorporated as an array of pointers. 274 */ 275 static const nvme_vuc_disc_t wdc_sn840_sn65x_vuc[] = { { 276 .nvd_short = "wdc/resize", 277 .nvd_desc = "drive resize", 278 .nvd_opc = WDC_VUC_RESIZE_OPC, 279 .nvd_impact = NVME_VUC_DISC_IMPACT_DATA | NVME_VUC_DISC_IMPACT_NS, 280 .nvd_dt = NVME_VUC_DISC_IO_NONE, 281 .nvd_lock = NVME_VUC_DISC_LOCK_WRITE 282 }, { 283 .nvd_short = "wdc/e6dump", 284 .nvd_desc = "dump e6 diagnostic data", 285 .nvd_opc = WDC_VUC_E6_DUMP_OPC, 286 .nvd_dt = NVME_VUC_DISC_IO_OUTPUT, 287 .nvd_lock = NVME_VUC_DISC_LOCK_READ 288 }, { 289 .nvd_short = "wdc/clear-assert", 290 .nvd_desc = "clear internal drive assertion", 291 .nvd_opc = WDC_VUC_ASSERT_OPC, 292 .nvd_dt = NVME_VUC_DISC_IO_NONE, 293 .nvd_lock = NVME_VUC_DISC_LOCK_NONE 294 }, { 295 /* 296 * It's hard to come up with a good impact statement from this. It will 297 * cause I/O to fail but may or may not cause issues with data. 298 */ 299 .nvd_short = "wdc/inject-assert", 300 .nvd_desc = "inject internal drive assertion", 301 .nvd_opc = WDC_VUC_ASSERT_OPC, 302 .nvd_dt = NVME_VUC_DISC_IO_NONE, 303 .nvd_lock = NVME_VUC_DISC_LOCK_WRITE 304 } }; 305 306 static const nvme_vsd_ident_t wdc_sn840_idents[] = { 307 { 308 .nvdi_vid = WDC_PCI_VID, 309 .nvdi_did = WDC_SN840_DID, 310 .nvdi_human = "WDC Ultrastar DC SN840", 311 } 312 }; 313 314 const nvme_vsd_t wdc_sn840 = { 315 .nvd_ident = wdc_sn840_idents, 316 .nvd_nident = ARRAY_SIZE(wdc_sn840_idents), 317 .nvd_logs = wdc_sn840_log_pages, 318 .nvd_nlogs = ARRAY_SIZE(wdc_sn840_log_pages), 319 .nvd_vuc = wdc_sn840_sn65x_vuc, 320 .nvd_nvuc = ARRAY_SIZE(wdc_sn840_sn65x_vuc) 321 }; 322 323 static const nvme_vsd_ident_t wdc_sn65x_idents[] = { 324 { 325 .nvdi_vid = WDC_PCI_VID, 326 .nvdi_did = WDC_SN650_DID, 327 .nvdi_human = "WDC Ultrastar DC SN650", 328 }, { 329 .nvdi_vid = WDC_PCI_VID, 330 .nvdi_did = WDC_SN655_DID, 331 .nvdi_human = "WDC Ultrastar DC SN655", 332 } 333 }; 334 335 const nvme_vsd_t wdc_sn65x = { 336 .nvd_ident = wdc_sn65x_idents, 337 .nvd_nident = ARRAY_SIZE(wdc_sn65x_idents), 338 .nvd_logs = wdc_sn65x_log_pages, 339 .nvd_nlogs = ARRAY_SIZE(wdc_sn65x_log_pages), 340 .nvd_vuc = wdc_sn840_sn65x_vuc, 341 .nvd_nvuc = ARRAY_SIZE(wdc_sn840_sn65x_vuc) 342 }; 343 344 static const nvme_vsd_ident_t wdc_sn861_idents[] = { 345 { 346 .nvdi_vid = WDC_PCI_VID, 347 .nvdi_did = WDC_SN861_DID, 348 .nvdi_human = "WDC Ultrastar DC SN861", 349 } 350 }; 351 352 static const nvme_log_page_info_t *wdc_sn861_log_pages[] = { 353 &ocp_log_smart, &ocp_log_errrec, &ocp_log_fwact, &ocp_log_lat, 354 &ocp_log_devcap, &ocp_log_unsup 355 }; 356 357 const nvme_vsd_t wdc_sn861 = { 358 .nvd_ident = wdc_sn861_idents, 359 .nvd_nident = ARRAY_SIZE(wdc_sn861_idents), 360 .nvd_logs = wdc_sn861_log_pages, 361 .nvd_nlogs = ARRAY_SIZE(wdc_sn861_log_pages), 362 }; 363 364 static nvme_vuc_req_t * 365 nvme_wdc_resize_vuc(nvme_ctrl_t *ctrl, uint8_t subcmd, uint32_t gib) 366 { 367 nvme_vuc_req_t *req = NULL; 368 uint32_t cdw12 = WDC_VUC_RESIZE_CMD | ((uint32_t)subcmd << 8); 369 370 if (!nvme_vendor_vuc_supported(ctrl, "wdc/resize")) { 371 return (false); 372 } 373 374 if (!nvme_vuc_req_init(ctrl, &req)) { 375 return (false); 376 } 377 378 if (!nvme_vuc_req_set_opcode(req, WDC_VUC_RESIZE_OPC) || 379 !nvme_vuc_req_set_cdw12(req, cdw12) || 380 !nvme_vuc_req_set_cdw13(req, gib) || 381 !nvme_vuc_req_set_timeout(req, nvme_wdc_resize_timeout)) { 382 nvme_vuc_req_fini(req); 383 return (false); 384 } 385 386 return (req); 387 } 388 389 bool 390 nvme_wdc_resize_get(nvme_ctrl_t *ctrl, uint32_t *gbp) 391 { 392 nvme_vuc_req_t *vuc; 393 394 if (gbp == NULL) { 395 return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0, 396 "encountered invalid uint32_t pointer: %p", gbp)); 397 } 398 399 if ((vuc = nvme_wdc_resize_vuc(ctrl, WDC_VUC_RESIZE_SUB_GET, 0)) == 400 NULL) { 401 return (false); 402 } 403 404 if (!nvme_vuc_req_exec(vuc)) { 405 nvme_vuc_req_fini(vuc); 406 return (false); 407 } 408 409 if (!nvme_vuc_req_get_cdw0(vuc, gbp)) { 410 nvme_vuc_req_fini(vuc); 411 return (false); 412 } 413 414 return (nvme_ctrl_success(ctrl)); 415 } 416 417 bool 418 nvme_wdc_resize_set(nvme_ctrl_t *ctrl, uint32_t gb) 419 { 420 nvme_vuc_req_t *vuc; 421 422 if ((vuc = nvme_wdc_resize_vuc(ctrl, WDC_VUC_RESIZE_SUB_SET, gb)) == 423 NULL) { 424 return (false); 425 } 426 427 if (!nvme_vuc_req_set_impact(vuc, NVME_VUC_DISC_IMPACT_DATA | 428 NVME_VUC_DISC_IMPACT_NS)) { 429 nvme_vuc_req_fini(vuc); 430 return (false); 431 } 432 433 if (!nvme_vuc_req_exec(vuc)) { 434 nvme_vuc_req_fini(vuc); 435 return (false); 436 } 437 438 nvme_vuc_req_fini(vuc); 439 return (nvme_ctrl_success(ctrl)); 440 } 441 442 void 443 nvme_wdc_e6_req_fini(nvme_wdc_e6_req_t *req) 444 { 445 if (req == NULL) { 446 return; 447 } 448 449 nvme_vuc_req_fini(req->wer_vuc); 450 req->wer_vuc = NULL; 451 free(req); 452 } 453 454 bool 455 nvme_wdc_e6_req_init(nvme_ctrl_t *ctrl, nvme_wdc_e6_req_t **reqp) 456 { 457 nvme_wdc_e6_req_t *req; 458 459 if (reqp == NULL) { 460 return (nvme_ctrl_error(ctrl, NVME_ERR_BAD_PTR, 0, 461 "encountered invalid nvme_commit_req_t output pointer: %p", 462 reqp)); 463 } 464 465 if (!nvme_vendor_vuc_supported(ctrl, "wdc/e6dump")) { 466 return (false); 467 } 468 469 req = calloc(1, sizeof (nvme_wdc_e6_req_t)); 470 if (req == NULL) { 471 int e = errno; 472 return (nvme_ctrl_error(ctrl, NVME_ERR_NO_MEM, e, "failed to " 473 "allocate memory for a new nvme_wdc_e6_req_t: %s", 474 strerror(e))); 475 } 476 477 if (!nvme_vuc_req_init(ctrl, &req->wer_vuc)) { 478 nvme_wdc_e6_req_fini(req); 479 return (false); 480 } 481 482 /* 483 * The documentation suggests we must explicitly set the mode in cdw12 484 * to zero. While that should be the default, we do anyways. 485 */ 486 if (!nvme_vuc_req_set_opcode(req->wer_vuc, WDC_VUC_E6_DUMP_OPC) || 487 !nvme_vuc_req_set_cdw12(req->wer_vuc, 0) || 488 !nvme_vuc_req_set_timeout(req->wer_vuc, nvme_wdc_e6_timeout)) { 489 nvme_wdc_e6_req_fini(req); 490 return (false); 491 } 492 493 for (size_t i = 0; i < ARRAY_SIZE(nvme_wdc_e6_req_fields); i++) { 494 if (nvme_wdc_e6_req_fields[i].nlfi_def_req) { 495 req->wer_need |= 1 << i; 496 } 497 } 498 499 *reqp = req; 500 return (nvme_ctrl_success(ctrl)); 501 } 502 503 static void 504 nvme_wdc_e6_req_set_need(nvme_wdc_e6_req_t *req, 505 nvme_wdc_e6_req_field_t field) 506 { 507 req->wer_need |= 1 << field; 508 } 509 510 static void 511 nvme_wdc_e6_req_clear_need(nvme_wdc_e6_req_t *req, 512 nvme_wdc_e6_req_field_t field) 513 { 514 req->wer_need &= ~(1 << field); 515 } 516 517 static const nvme_field_check_t nvme_wdc_e6_check_off = { 518 nvme_wdc_e6_req_fields, NVME_WDC_E6_REQ_FIELD_OFFSET, 519 NVME_ERR_WDC_E6_OFFSET_RANGE, 0, 0 520 }; 521 522 bool 523 nvme_wdc_e6_req_set_offset(nvme_wdc_e6_req_t *req, uint64_t off) 524 { 525 nvme_ctrl_t *ctrl = req->wer_vuc->nvr_ctrl; 526 uint32_t ndw; 527 528 if (!nvme_field_check_one(ctrl, off, "e6 dump", &nvme_wdc_e6_check_off, 529 0)) { 530 return (false); 531 } 532 533 ndw = off >> 2; 534 if (!nvme_vuc_req_set_cdw13(req->wer_vuc, ndw)) { 535 return (false); 536 } 537 538 nvme_wdc_e6_req_clear_need(req, NVME_WDC_E6_REQ_FIELD_OFFSET); 539 return (nvme_ctrl_success(ctrl)); 540 } 541 542 bool 543 nvme_wdc_e6_req_set_output(nvme_wdc_e6_req_t *req, void *buf, size_t len) 544 { 545 nvme_ctrl_t *ctrl = req->wer_vuc->nvr_ctrl; 546 547 /* 548 * The set output validation handling takes care of all the actual 549 * normal field validation work that we need. 550 */ 551 if (!nvme_vuc_req_set_output(req->wer_vuc, buf, len)) { 552 return (false); 553 } 554 555 nvme_wdc_e6_req_clear_need(req, NVME_WDC_E6_REQ_FIELD_LEN); 556 return (nvme_ctrl_success(ctrl)); 557 } 558 559 bool 560 nvme_wdc_e6_req_clear_output(nvme_wdc_e6_req_t *req) 561 { 562 nvme_ctrl_t *ctrl = req->wer_vuc->nvr_ctrl; 563 564 if (!nvme_vuc_req_clear_output(req->wer_vuc)) { 565 return (false); 566 } 567 568 nvme_wdc_e6_req_set_need(req, NVME_WDC_E6_REQ_FIELD_LEN); 569 return (nvme_ctrl_success(ctrl)); 570 } 571 572 bool 573 nvme_wdc_e6_req_exec(nvme_wdc_e6_req_t *req) 574 { 575 nvme_ctrl_t *ctrl = req->wer_vuc->nvr_ctrl; 576 577 if (req->wer_need != 0) { 578 return (nvme_field_miss_err(ctrl, nvme_wdc_e6_req_fields, 579 ARRAY_SIZE(nvme_wdc_e6_req_fields), 580 NVME_ERR_WDC_E6_REQ_MISSING_FIELDS, "wdc e6", 581 req->wer_need)); 582 } 583 584 if (!nvme_vuc_req_exec(req->wer_vuc)) { 585 return (false); 586 } 587 588 return (nvme_ctrl_success(ctrl)); 589 } 590 591 static bool 592 nvme_wdc_assert_common(nvme_ctrl_t *ctrl, uint32_t subcmd) 593 { 594 nvme_vuc_req_t *req = NULL; 595 const char *name = subcmd == WDC_VUC_ASSERT_SUB_CLEAR ? 596 "wdc/clear-assert" : "wdc/inject-assert"; 597 uint32_t cdw12 = WDC_VUC_ASSERT_CMD | (subcmd << 8); 598 599 if (!nvme_vendor_vuc_supported(ctrl, name)) { 600 return (false); 601 } 602 603 if (!nvme_vuc_req_init(ctrl, &req)) { 604 return (false); 605 } 606 607 if (!nvme_vuc_req_set_opcode(req, WDC_VUC_ASSERT_OPC) || 608 !nvme_vuc_req_set_cdw12(req, cdw12) || 609 !nvme_vuc_req_set_timeout(req, nvme_wdc_assert_timeout) || 610 !nvme_vuc_req_exec(req)) { 611 nvme_vuc_req_fini(req); 612 return (false); 613 } 614 615 nvme_vuc_req_fini(req); 616 return (nvme_ctrl_success(ctrl)); 617 } 618 619 bool 620 nvme_wdc_assert_clear(nvme_ctrl_t *ctrl) 621 { 622 return (nvme_wdc_assert_common(ctrl, WDC_VUC_ASSERT_SUB_CLEAR)); 623 } 624 625 bool 626 nvme_wdc_assert_inject(nvme_ctrl_t *ctrl) 627 { 628 return (nvme_wdc_assert_common(ctrl, WDC_VUC_ASSERT_SUB_INJECT)); 629 } 630