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