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 2026 Oxide Computer Company 14 */ 15 16 #ifndef _LIBNVME_IMPL_H 17 #define _LIBNVME_IMPL_H 18 19 /* 20 * Implementation structures and related for libnvme. 21 */ 22 23 #include <libnvme.h> 24 #include <libdevinfo.h> 25 #include <stdbool.h> 26 #include <nvme_common.h> 27 #include <synch.h> 28 #include <sys/nvme/ocp.h> 29 30 #ifdef __cplusplus 31 extern "C" { 32 #endif 33 34 /* 35 * Maximum size of an internal error message. 36 */ 37 #define NVME_ERR_LEN 1024 38 39 typedef struct nvme_err_data { 40 nvme_err_t ne_err; 41 int32_t ne_syserr; 42 char ne_errmsg[NVME_ERR_LEN]; 43 size_t ne_errlen; 44 uint32_t ne_ctrl_sct; 45 uint32_t ne_ctrl_sc; 46 } nvme_err_data_t; 47 48 struct nvme { 49 nvme_err_data_t nh_err; 50 di_node_t nh_devinfo; 51 }; 52 53 struct nvme_ctrl_disc { 54 di_node_t ncd_devi; 55 di_minor_t ncd_minor; 56 }; 57 58 struct nvme_ctrl_iter { 59 nvme_t *ni_nvme; 60 bool ni_done; 61 di_node_t ni_cur; 62 nvme_ctrl_disc_t ni_disc; 63 }; 64 65 typedef enum { 66 /* 67 * This indicates that we have attempted to fill in the NVMe 2.0 68 * supported logs page information and therefore can use it as part of 69 * log page discovery. This is filled in lazily on a handle and will 70 * persist as long as a handle does. If the log page is not supported or 71 * an error occurred then the VALID flag will be set, but the 72 * nc_sup_logs member will be set to NULL to indicate that we don't have 73 * the information. 74 * 75 * When the log page is supported, but something has gone wrong, we will 76 * set the FAILED flag to indicate that. Presuming it wasn't a memory 77 * failure, then we try to save a copy of the resulting nvme_err_data_t. 78 * This information isn't exposed outside of the library, but is kept on 79 * the handle to aid debugging. 80 */ 81 NVME_CTRL_F_SUP_LOGS_VALID = 1 << 0, 82 NVME_CTRL_F_SUP_LOGS_FAILED = 1 << 1 83 } nvme_ctrl_flags_t; 84 85 struct nvme_ctrl { 86 nvme_t *nc_nvme; 87 nvme_err_data_t nc_err; 88 di_node_t nc_devi; 89 di_minor_t nc_minor; 90 char *nc_devi_path; 91 int32_t nc_inst; 92 int nc_fd; 93 nvme_version_t nc_vers; 94 nvme_identify_ctrl_t nc_info; 95 const struct nvme_vsd *nc_vsd; 96 nvme_ctrl_flags_t nc_flags; 97 nvme_suplog_log_t *nc_sup_logs; 98 nvme_err_data_t *nc_sup_logs_err; 99 }; 100 101 struct nvme_ns_disc { 102 uint32_t nnd_nsid; 103 nvme_ns_disc_level_t nnd_level; 104 nvme_ns_disc_flags_t nnd_flags; 105 uint8_t nnd_eui64[8]; 106 uint8_t nnd_nguid[16]; 107 }; 108 109 struct nvme_ns_iter { 110 nvme_ctrl_t *nni_ctrl; 111 nvme_ns_disc_level_t nni_level; 112 bool nni_err; 113 bool nni_done; 114 size_t nni_cur_idx; 115 nvme_ns_disc_t nni_disc; 116 }; 117 118 struct nvme_ns { 119 nvme_ctrl_t *nn_ctrl; 120 uint32_t nn_nsid; 121 }; 122 123 struct nvme_nvm_lba_fmt { 124 uint32_t nnlf_id; 125 uint32_t nnlf_ms; 126 uint64_t nnlf_lbasz; 127 uint32_t nnlf_rel; 128 }; 129 130 struct nvme_ctrl_info { 131 nvme_info_err_t nci_err; 132 int32_t nci_syserr; 133 char nci_errmsg[NVME_ERR_LEN]; 134 size_t nci_errlen; 135 /* 136 * The NVMe strings are generally ASCII strings that have trailing 137 * spaces on them ala SCSI. We transform that into a C style string 138 * without trailing padding. The +1 assumes we need to add a terminator. 139 */ 140 char nci_serial[NVME_SERIAL_SZ + 1]; 141 char nci_model[NVME_MODEL_SZ + 1]; 142 char nci_fwrev[NVME_FWVER_SZ + 1]; 143 bool nci_lbaf_valid[NVME_MAX_LBAF]; 144 nvme_nvm_lba_fmt_t nci_lbaf[NVME_MAX_LBAF]; 145 /* 146 * Only information below here should be persisted. That is, the above 147 * information is meant to be specific to the library. 148 */ 149 nvme_version_t nci_vers; 150 int32_t nci_inst; 151 char nci_dev_path[PATH_MAX]; 152 nvme_identify_ctrl_t nci_info; 153 nvme_identify_nsid_t nci_ns; 154 nvme_ctrl_transport_t nci_tport; 155 uint16_t nci_vid; 156 uint16_t nci_did; 157 uint16_t nci_subvid; 158 uint16_t nci_subsys; 159 uint8_t nci_rev; 160 uint32_t nci_mps_min; 161 uint32_t nci_mps_max; 162 uint32_t nci_nintrs; 163 }; 164 165 /* 166 * Internal nvlist_t keys for control information. 167 */ 168 #define NVME_NVL_CI_VERS "version" 169 #define NVME_NVL_CI_VERS_0 0 170 #define NVME_NVL_CI_INST "inst" 171 #define NVME_NVL_CI_MAJOR "nvme-major-version" 172 #define NVME_NVL_CI_MINOR "nvme-minor-version" 173 #define NVME_NVL_CI_DEV_PATH "dev-path" 174 #define NVME_NVL_CI_ID_CTRL "identify-controller" 175 #define NVME_NVL_CI_ID_NS "identify-namespace" 176 #define NVME_NVL_CI_TPORT "transport" 177 #define NVME_NVL_CI_PCI_VID "pci-vendor-id" 178 #define NVME_NVL_CI_PCI_DID "pci-device-id" 179 #define NVME_NVL_CI_PCI_SUBVID "pci-subsystem-vendor-id" 180 #define NVME_NVL_CI_PCI_SUBSYS "pci-subsystem-id" 181 #define NVME_NVL_CI_PCI_REV "pci-revision-id" 182 #define NVME_NVL_CI_PCI_MPSMIN "pci-memory-page-size-min" 183 #define NVME_NVL_CI_PCI_MPSMAX "pci-memory-page-size-max" 184 #define NVME_NVL_CI_PCI_NINTRS "pci-num-interrupts" 185 186 struct nvme_ns_info { 187 nvme_info_err_t nni_err; 188 int32_t nni_syserr; 189 char nni_errmsg[NVME_ERR_LEN]; 190 size_t nni_errlen; 191 uint32_t nni_nsid; 192 nvme_version_t nni_vers; 193 nvme_ns_disc_level_t nni_level; 194 nvme_ioctl_ns_info_t nni_info; 195 bool nni_lbaf_valid[NVME_MAX_LBAF]; 196 nvme_nvm_lba_fmt_t nni_lbaf[NVME_MAX_LBAF]; 197 }; 198 199 typedef enum { 200 NVME_LOG_REQ_F_RAE = 1 << 0, 201 NVME_LOG_REQ_F_BCAST_NS_OK = 1 << 1 202 } nvme_log_req_flags_t; 203 204 struct nvme_log_req { 205 nvme_ctrl_t *nlr_ctrl; 206 uint32_t nlr_need; 207 uint32_t nlr_allow; 208 nvme_csi_t nlr_csi; 209 uint32_t nlr_lid; 210 uint32_t nlr_lsp; 211 uint32_t nlr_lsi; 212 uint32_t nlr_nsid; 213 nvme_log_req_flags_t nlr_flags; 214 void *nlr_output; 215 size_t nlr_output_len; 216 uint64_t nlr_offset; 217 }; 218 219 /* 220 * This structure is used internally to describe information about a given log 221 * page. 222 */ 223 typedef enum { 224 /* 225 * This indicates that the log page is actually implemented. 226 */ 227 NVME_LOG_DISC_F_IMPL = 1 << 0 228 } nvme_log_disc_flags_t; 229 230 struct nvme_log_disc { 231 const char *nld_short; 232 const char *nld_desc; 233 const char *const *nld_aliases; 234 size_t nld_naliases; 235 uint32_t nld_lid; 236 nvme_csi_t nld_csi; 237 nvme_log_disc_kind_t nld_kind; 238 nvme_log_disc_source_t nld_srcs; 239 nvme_log_disc_fields_t nld_fields; 240 nvme_log_disc_scope_t nld_scope; 241 nvme_log_disc_flags_t nld_flags; 242 nvme_log_size_kind_t nld_size_kind; 243 uint64_t nld_alloc_len; 244 nvme_log_page_var_len_f nld_var_func; 245 }; 246 247 struct nvme_log_iter { 248 nvme_ctrl_t *nli_ctrl; 249 nvme_log_disc_scope_t nli_scope; 250 bool nli_std_done; 251 bool nli_vs_done; 252 size_t nli_cur_idx; 253 nvme_log_disc_t nli_nld; 254 }; 255 256 /* 257 * Feature discovery and iteration. 258 */ 259 struct nvme_feat_disc { 260 const char *nfd_short; 261 const char *nfd_spec; 262 uint32_t nfd_fid; 263 nvme_feat_kind_t nfd_kind; 264 nvme_feat_scope_t nfd_scope; 265 nvme_feat_flags_t nfd_flags; 266 nvme_feat_csi_t nfd_csi; 267 nvme_get_feat_fields_t nfd_in_get; 268 nvme_set_feat_fields_t nfd_in_set; 269 nvme_feat_output_t nfd_out_get; 270 nvme_feat_output_t nfd_out_set; 271 nvme_disc_impact_t nfd_impact; 272 uint64_t nfd_len; 273 nvme_feat_impl_t nfd_impl; 274 }; 275 276 struct nvme_feat_iter { 277 nvme_ctrl_t *nfi_ctrl; 278 nvme_feat_scope_t nfi_scope; 279 bool nfi_std_done; 280 bool nfi_vs_done; 281 size_t nfi_cur_idx; 282 nvme_feat_disc_t nfi_disc; 283 }; 284 285 struct nvme_get_feat_req { 286 nvme_ctrl_t *gfr_ctrl; 287 uint32_t gfr_need; 288 uint32_t gfr_allow; 289 nvme_feat_flags_t gfr_flags; 290 uint32_t gfr_fid; 291 uint32_t gfr_sel; 292 uint32_t gfr_nsid; 293 uint32_t gfr_cdw11; 294 void *gfr_buf; 295 size_t gfr_len; 296 uint64_t gfr_targ_len; 297 /* 298 * The following are set on exec. 299 */ 300 bool gfr_results_valid; 301 uint32_t gfr_cdw0; 302 }; 303 304 struct nvme_set_feat_req { 305 nvme_ctrl_t *sfr_ctrl; 306 uint32_t sfr_need; 307 uint32_t sfr_allow; 308 nvme_feat_flags_t sfr_flags; 309 uint32_t sfr_fid; 310 uint32_t sfr_save; 311 uint32_t sfr_cdw11; 312 uint32_t sfr_cdw12; 313 uint32_t sfr_cdw13; 314 uint32_t sfr_cdw15; 315 const void *sfr_buf; 316 size_t sfr_len; 317 uint64_t sfr_targ_len; 318 uint32_t sfr_nsid; 319 uint32_t sfr_impact; 320 /* 321 * The following are set on exec. 322 */ 323 bool sfr_results_valid; 324 uint32_t sfr_cdw0; 325 }; 326 327 /* 328 * Identify command request 329 */ 330 struct nvme_id_req { 331 nvme_ctrl_t *nir_ctrl; 332 const nvme_identify_info_t *nir_info; 333 nvme_identify_req_field_t nir_need; 334 nvme_identify_req_field_t nir_allow; 335 uint32_t nir_nsid; 336 uint32_t nir_ctrlid; 337 void *nir_buf; 338 }; 339 340 /* 341 * Vendor unique command support. 342 */ 343 struct nvme_vuc_disc { 344 const char *nvd_short; 345 const char *nvd_desc; 346 uint8_t nvd_opc; 347 nvme_disc_impact_t nvd_impact; 348 nvme_vuc_disc_io_t nvd_dt; 349 nvme_vuc_disc_lock_t nvd_lock; 350 }; 351 352 struct nvme_vuc_iter { 353 nvme_ctrl_t *nvi_ctrl; 354 size_t nvi_cur_idx; 355 }; 356 357 struct nvme_vuc_req { 358 nvme_ctrl_t *nvr_ctrl; 359 uint32_t nvr_need; 360 uint32_t nvr_opcode; 361 uint32_t nvr_timeout; 362 uint32_t nvr_nsid; 363 uint32_t nvr_cdw12; 364 uint32_t nvr_cdw13; 365 uint32_t nvr_cdw14; 366 uint32_t nvr_cdw15; 367 uint32_t nvr_impact; 368 size_t nvr_outlen; 369 size_t nvr_inlen; 370 void *nvr_output; 371 const void *nvr_input; 372 /* 373 * The following values are set on exec. 374 */ 375 bool nvr_results_valid; 376 uint32_t nvr_cdw0; 377 }; 378 379 /* 380 * If we ever support updating the boot partition ID, our expectation is that we 381 * end up doing that through other library interfaces even if it uses the same 382 * underlying ioctl. That ultimately will keep things simpler from a consumer 383 * perspective. 384 */ 385 struct nvme_fw_commit_req { 386 nvme_ctrl_t *fwc_ctrl; 387 uint32_t fwc_need; 388 uint32_t fwc_slot; 389 uint32_t fwc_action; 390 }; 391 392 /* 393 * Format request data. 394 */ 395 struct nvme_format_req { 396 nvme_ctrl_t *nfr_ctrl; 397 uint32_t nfr_need; 398 bool nfr_ns; 399 uint32_t nfr_lbaf; 400 uint32_t nfr_ses; 401 uint32_t nfr_nsid; 402 }; 403 404 /* 405 * Namespace Attach request. 406 */ 407 struct nvme_ns_attach_req { 408 nvme_ctrl_t *nar_ctrl; 409 uint32_t nar_need; 410 uint32_t nar_nsid; 411 uint32_t nar_sel; 412 }; 413 414 /* 415 * Namespace Delete request. 416 */ 417 struct nvme_ns_delete_req { 418 nvme_ctrl_t *ndr_ctrl; 419 uint32_t ndr_need; 420 uint32_t ndr_nsid; 421 }; 422 423 /* 424 * Namespace Create request. 425 */ 426 struct nvme_ns_create_req { 427 nvme_ctrl_t *ncr_ctrl; 428 nvme_csi_t ncr_csi; 429 uint32_t ncr_need; 430 uint32_t ncr_allow; 431 uint64_t ncr_nsze; 432 uint64_t ncr_ncap; 433 uint32_t ncr_flbas; 434 uint32_t ncr_nmic; 435 /* 436 * The following are set on exec. 437 */ 438 bool ncr_results_valid; 439 uint32_t ncr_nsid; 440 }; 441 442 /* 443 * WDC e6 request. This was made an opaque request style structure to try to 444 * safeguard us against future changes where something like the optional mode 445 * byte was required (right now it's just always zero). 446 */ 447 struct nvme_wdc_e6_req { 448 uint32_t wer_need; 449 nvme_vuc_req_t *wer_vuc; 450 }; 451 452 /* 453 * OCP Error injection request. 454 */ 455 struct nvme_ocp_errinj_req { 456 uint32_t oer_need; 457 uint32_t oer_allow; 458 ocp_vuf_errinj_t oer_err[OCP_ERRINJ_MAX_INJECT + 1]; 459 nvme_set_feat_req_t *oer_feat; 460 }; 461 462 /* 463 * Common interfaces for operation success and failure. There are currently 464 * errors that can exist on four different objects in the library and there is 465 * one success() and error() function for each of them. See the theory statement 466 * section on errors in libnvme.c for more information. Note, all namespace and 467 * request structures set errors on the controller. 468 * 469 * The controller has an extra error path that is used for converting ioctls to 470 * semantic errors. It takes care of translating the different kinds of kernel 471 * errors to the library's errors. Our goal is to never programmatically leak 472 * the kernel ioctls and their error codes as they do not promise stability 473 * unlike our aspirations. It also doesn't allow for variable arguments and only 474 * takes a single description. 475 */ 476 extern bool nvme_error(nvme_t *, nvme_err_t, int32_t, const char *, 477 ...) __PRINTFLIKE(4); 478 extern bool nvme_success(nvme_t *); 479 480 extern bool nvme_ctrl_error(nvme_ctrl_t *, nvme_err_t, int32_t, const char *, 481 ...) __PRINTFLIKE(4); 482 extern bool nvme_ioctl_error(nvme_ctrl_t *, const nvme_ioctl_common_t *, 483 const char *); 484 extern bool nvme_ioctl_syserror(nvme_ctrl_t *, int, const char *); 485 extern bool nvme_ctrl_success(nvme_ctrl_t *); 486 487 extern bool nvme_info_error(nvme_ctrl_info_t *, nvme_info_err_t, int32_t, 488 const char *, ...) __PRINTFLIKE(4); 489 extern bool nvme_info_success(nvme_ctrl_info_t *); 490 491 extern bool nvme_ns_info_error(nvme_ns_info_t *, nvme_info_err_t, int32_t, 492 const char *, ...) __PRINTFLIKE(4); 493 extern bool nvme_ns_info_success(nvme_ns_info_t *); 494 495 /* 496 * Common functions for preserving and restoring error data. This comes up when 497 * utilizing callback functions for discovery where we call libnvme functions. 498 */ 499 extern void nvme_err_save(const nvme_t *, nvme_err_data_t *); 500 extern void nvme_err_set(nvme_t *, const nvme_err_data_t *); 501 extern void nvme_ctrl_err_save(const nvme_ctrl_t *, nvme_err_data_t *); 502 extern void nvme_ctrl_err_set(nvme_ctrl_t *, const nvme_err_data_t *); 503 504 /* 505 * Common functions for issuing ioctls to a controller. 506 */ 507 extern bool nvme_ioc_ctrl_info(nvme_ctrl_t *, nvme_ioctl_ctrl_info_t *); 508 extern bool nvme_ioc_ns_info(nvme_ctrl_t *, uint32_t, nvme_ioctl_ns_info_t *); 509 510 /* 511 * Common validation template functions. 512 */ 513 extern bool nvme_field_miss_err(nvme_ctrl_t *, const nvme_field_info_t *, 514 size_t, nvme_err_t, const char *, uint32_t); 515 516 typedef struct { 517 const nvme_field_info_t *chk_fields; 518 size_t chk_index; 519 nvme_err_t chk_field_range; 520 nvme_err_t chk_field_unsup; 521 nvme_err_t chk_field_unuse; 522 } nvme_field_check_t; 523 524 extern bool nvme_field_check_one(nvme_ctrl_t *, uint64_t, const char *, 525 const nvme_field_check_t *, uint32_t allow); 526 extern bool nvme_field_valid_impact(nvme_ctrl_t *, nvme_disc_impact_t, 527 nvme_err_t); 528 529 /* 530 * Misc. functions. 531 */ 532 extern const char *nvme_tporttostr(nvme_ctrl_transport_t); 533 extern nvme_ns_disc_level_t nvme_ns_state_to_disc_level(nvme_ns_state_t); 534 extern const char *nvme_nsleveltostr(nvme_ns_disc_level_t); 535 536 /* 537 * Version related information and functions. There are statically declared 538 * version structures in the library for use for internal comparisons. Note, we 539 * have attempted to avoid a general comparison function in the internal API so 540 * that way it's always clear what we're comparing to a version and can't 541 * reverse things. 542 */ 543 extern const nvme_version_t nvme_vers_1v0; 544 extern const nvme_version_t nvme_vers_1v1; 545 extern const nvme_version_t nvme_vers_1v2; 546 extern const nvme_version_t nvme_vers_1v3; 547 extern const nvme_version_t nvme_vers_1v4; 548 extern const nvme_version_t nvme_vers_2v0; 549 550 extern bool nvme_vers_ctrl_atleast(const nvme_ctrl_t *, const nvme_version_t *); 551 extern bool nvme_vers_ctrl_info_atleast(const nvme_ctrl_info_t *, 552 const nvme_version_t *); 553 extern bool nvme_vers_ns_info_atleast(const nvme_ns_info_t *, 554 const nvme_version_t *); 555 556 /* 557 * Vendor-specific information. 558 */ 559 typedef struct nvme_vsd_ident { 560 const char *nvdi_human; 561 bool nvdi_subsys; 562 uint16_t nvdi_vid; 563 uint16_t nvdi_did; 564 uint16_t nvdi_svid; 565 uint16_t nvdi_sdid; 566 } nvme_vsd_ident_t; 567 568 typedef struct nvme_vsd { 569 const nvme_vsd_ident_t *nvd_ident; 570 size_t nvd_nident; 571 const nvme_log_page_info_t *const *nvd_logs; 572 size_t nvd_nlogs; 573 const nvme_vuc_disc_t *nvd_vuc; 574 size_t nvd_nvuc; 575 const nvme_feat_info_t *const *nvd_feats; 576 size_t nvd_nfeats; 577 } nvme_vsd_t; 578 579 extern const nvme_log_page_info_t ocp_log_smart; 580 extern const nvme_log_page_info_t ocp_log_errrec; 581 extern const nvme_log_page_info_t ocp_log_fwact; 582 extern const nvme_log_page_info_t ocp_log_lat; 583 extern const nvme_log_page_info_t ocp_log_devcap; 584 extern const nvme_log_page_info_t ocp_log_unsup; 585 extern const nvme_log_page_info_t ocp_log_hwcomp; 586 extern const nvme_log_page_info_t ocp_log_telstr; 587 588 extern const nvme_feat_info_t ocp_feat_errinj; 589 extern const nvme_feat_info_t ocp_feat_plpfail; 590 extern const nvme_feat_info_t ocp_feat_plphealth; 591 592 extern const nvme_vsd_t wdc_sn840; 593 extern const nvme_vsd_t wdc_sn65x; 594 extern const nvme_vsd_t sandisk_sn861; 595 extern const nvme_vsd_t micron_7300; 596 extern const nvme_vsd_t micron_74x0; 597 extern const nvme_vsd_t micron_x500; 598 extern const nvme_vsd_t micron_7600; 599 extern const nvme_vsd_t micron_9550; 600 extern const nvme_vsd_t intel_p5510; 601 extern const nvme_vsd_t solidigm_p5x20; 602 extern const nvme_vsd_t solidigm_ps10x0; 603 extern const nvme_vsd_t kioxia_cd8; 604 extern const nvme_vsd_t phison_x200; 605 extern const nvme_vsd_t samsung_pm9d3a; 606 607 extern void nvme_vendor_map_ctrl(nvme_ctrl_t *); 608 extern bool nvme_vendor_vuc_supported(nvme_ctrl_t *, const char *); 609 extern bool nvme_vendor_feature_supported(nvme_ctrl_t *, const char *); 610 611 /* 612 * Internal formatting functions that probably could be external. 613 */ 614 #define NVME_NGUID_NAMELEN 33 615 #define NVME_EUI64_NAMELEN 17 616 617 extern int nvme_format_nguid(const uint8_t [16], char *, size_t); 618 extern int nvme_format_eui64(const uint8_t [8], char *, size_t); 619 620 #ifdef __cplusplus 621 } 622 #endif 623 624 #endif /* _LIBNVME_IMPL_H */ 625