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 #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 uint32_t nld_lid; 233 nvme_csi_t nld_csi; 234 nvme_log_disc_kind_t nld_kind; 235 nvme_log_disc_source_t nld_srcs; 236 nvme_log_disc_fields_t nld_fields; 237 nvme_log_disc_scope_t nld_scope; 238 nvme_log_disc_flags_t nld_flags; 239 nvme_log_size_kind_t nld_size_kind; 240 uint64_t nld_alloc_len; 241 nvme_log_page_var_len_f nld_var_func; 242 }; 243 244 struct nvme_log_iter { 245 nvme_ctrl_t *nli_ctrl; 246 nvme_log_disc_scope_t nli_scope; 247 bool nli_std_done; 248 bool nli_vs_done; 249 size_t nli_cur_idx; 250 nvme_log_disc_t nli_nld; 251 }; 252 253 /* 254 * Feature discovery and iteration. 255 */ 256 struct nvme_feat_disc { 257 const char *nfd_short; 258 const char *nfd_spec; 259 uint32_t nfd_fid; 260 nvme_feat_kind_t nfd_kind; 261 nvme_feat_scope_t nfd_scope; 262 nvme_feat_flags_t nfd_flags; 263 nvme_feat_csi_t nfd_csi; 264 nvme_get_feat_fields_t nfd_in_get; 265 nvme_set_feat_fields_t nfd_in_set; 266 nvme_feat_output_t nfd_out_get; 267 nvme_feat_output_t nfd_out_set; 268 uint64_t nfd_len; 269 nvme_feat_impl_t nfd_impl; 270 }; 271 272 struct nvme_feat_iter { 273 nvme_ctrl_t *nfi_ctrl; 274 nvme_feat_scope_t nfi_scope; 275 size_t nfi_cur_idx; 276 nvme_feat_disc_t nfi_disc; 277 }; 278 279 struct nvme_get_feat_req { 280 nvme_ctrl_t *gfr_ctrl; 281 uint32_t gfr_need; 282 uint32_t gfr_allow; 283 nvme_feat_flags_t gfr_flags; 284 uint32_t gfr_fid; 285 uint32_t gfr_sel; 286 uint32_t gfr_nsid; 287 uint32_t gfr_cdw11; 288 void *gfr_buf; 289 size_t gfr_len; 290 uint64_t gfr_targ_len; 291 /* 292 * The following are set on exec. 293 */ 294 bool gfr_results_valid; 295 uint32_t gfr_cdw0; 296 }; 297 298 /* 299 * Identify command request 300 */ 301 struct nvme_id_req { 302 nvme_ctrl_t *nir_ctrl; 303 const nvme_identify_info_t *nir_info; 304 nvme_identify_req_field_t nir_need; 305 nvme_identify_req_field_t nir_allow; 306 uint32_t nir_nsid; 307 uint32_t nir_ctrlid; 308 void *nir_buf; 309 }; 310 311 /* 312 * Vendor unique command support. 313 */ 314 struct nvme_vuc_disc { 315 const char *nvd_short; 316 const char *nvd_desc; 317 uint8_t nvd_opc; 318 nvme_vuc_disc_impact_t nvd_impact; 319 nvme_vuc_disc_io_t nvd_dt; 320 nvme_vuc_disc_lock_t nvd_lock; 321 }; 322 323 struct nvme_vuc_iter { 324 nvme_ctrl_t *nvi_ctrl; 325 size_t nvi_cur_idx; 326 }; 327 328 struct nvme_vuc_req { 329 nvme_ctrl_t *nvr_ctrl; 330 uint32_t nvr_need; 331 uint32_t nvr_opcode; 332 uint32_t nvr_timeout; 333 uint32_t nvr_nsid; 334 uint32_t nvr_cdw12; 335 uint32_t nvr_cdw13; 336 uint32_t nvr_cdw14; 337 uint32_t nvr_cdw15; 338 uint32_t nvr_impact; 339 size_t nvr_outlen; 340 size_t nvr_inlen; 341 void *nvr_output; 342 const void *nvr_input; 343 /* 344 * The following values are set on exec. 345 */ 346 bool nvr_results_valid; 347 uint32_t nvr_cdw0; 348 }; 349 350 /* 351 * If we ever support updating the boot partition ID, our expectation is that we 352 * end up doing that through other library interfaces even if it uses the same 353 * underlying ioctl. That ultimately will keep things simpler from a consumer 354 * perspective. 355 */ 356 struct nvme_fw_commit_req { 357 nvme_ctrl_t *fwc_ctrl; 358 uint32_t fwc_need; 359 uint32_t fwc_slot; 360 uint32_t fwc_action; 361 }; 362 363 /* 364 * Format request data. 365 */ 366 struct nvme_format_req { 367 nvme_ctrl_t *nfr_ctrl; 368 uint32_t nfr_need; 369 bool nfr_ns; 370 uint32_t nfr_lbaf; 371 uint32_t nfr_ses; 372 uint32_t nfr_nsid; 373 }; 374 375 /* 376 * Namespace Attach request. 377 */ 378 struct nvme_ns_attach_req { 379 nvme_ctrl_t *nar_ctrl; 380 uint32_t nar_need; 381 uint32_t nar_nsid; 382 uint32_t nar_sel; 383 }; 384 385 /* 386 * Namespace Delete request. 387 */ 388 struct nvme_ns_delete_req { 389 nvme_ctrl_t *ndr_ctrl; 390 uint32_t ndr_need; 391 uint32_t ndr_nsid; 392 }; 393 394 /* 395 * Namespace Create request. 396 */ 397 struct nvme_ns_create_req { 398 nvme_ctrl_t *ncr_ctrl; 399 nvme_csi_t ncr_csi; 400 uint32_t ncr_need; 401 uint32_t ncr_allow; 402 uint64_t ncr_nsze; 403 uint64_t ncr_ncap; 404 uint32_t ncr_flbas; 405 uint32_t ncr_nmic; 406 /* 407 * The following are set on exec. 408 */ 409 bool ncr_results_valid; 410 uint32_t ncr_nsid; 411 }; 412 413 /* 414 * WDC e6 request. This was made an opaque request style structure to try to 415 * safeguard us against future changes where something like the optional mode 416 * byte was required (right now it's just always zero). 417 */ 418 struct nvme_wdc_e6_req { 419 uint32_t wer_need; 420 nvme_vuc_req_t *wer_vuc; 421 }; 422 423 /* 424 * Common interfaces for operation success and failure. There are currently 425 * errors that can exist on four different objects in the library and there is 426 * one success() and error() function for each of them. See the theory statement 427 * section on errors in libnvme.c for more information. Note, all namespace and 428 * request structures set errors on the controller. 429 * 430 * The controller has an extra error path that is used for converting ioctls to 431 * semantic errors. It takes care of translating the different kinds of kernel 432 * errors to the library's errors. Our goal is to never programmatically leak 433 * the kernel ioctls and their error codes as they do not promise stability 434 * unlike our aspirations. It also doesn't allow for variable arguments and only 435 * takes a single description. 436 */ 437 extern bool nvme_error(nvme_t *, nvme_err_t, int32_t, const char *, 438 ...) __PRINTFLIKE(4); 439 extern bool nvme_success(nvme_t *); 440 441 extern bool nvme_ctrl_error(nvme_ctrl_t *, nvme_err_t, int32_t, const char *, 442 ...) __PRINTFLIKE(4); 443 extern bool nvme_ioctl_error(nvme_ctrl_t *, const nvme_ioctl_common_t *, 444 const char *); 445 extern bool nvme_ioctl_syserror(nvme_ctrl_t *, int, const char *); 446 extern bool nvme_ctrl_success(nvme_ctrl_t *); 447 448 extern bool nvme_info_error(nvme_ctrl_info_t *, nvme_info_err_t, int32_t, 449 const char *, ...) __PRINTFLIKE(4); 450 extern bool nvme_info_success(nvme_ctrl_info_t *); 451 452 extern bool nvme_ns_info_error(nvme_ns_info_t *, nvme_info_err_t, int32_t, 453 const char *, ...) __PRINTFLIKE(4); 454 extern bool nvme_ns_info_success(nvme_ns_info_t *); 455 456 /* 457 * Common functions for preserving and restoring error data. This comes up when 458 * utilizing callback functions for discovery where we call libnvme functions. 459 */ 460 extern void nvme_err_save(const nvme_t *, nvme_err_data_t *); 461 extern void nvme_err_set(nvme_t *, const nvme_err_data_t *); 462 extern void nvme_ctrl_err_save(const nvme_ctrl_t *, nvme_err_data_t *); 463 extern void nvme_ctrl_err_set(nvme_ctrl_t *, const nvme_err_data_t *); 464 465 /* 466 * Common functions for issuing ioctls to a controller. 467 */ 468 extern bool nvme_ioc_ctrl_info(nvme_ctrl_t *, nvme_ioctl_ctrl_info_t *); 469 extern bool nvme_ioc_ns_info(nvme_ctrl_t *, uint32_t, nvme_ioctl_ns_info_t *); 470 471 /* 472 * Common validation template functions. 473 */ 474 extern bool nvme_field_miss_err(nvme_ctrl_t *, const nvme_field_info_t *, 475 size_t, nvme_err_t, const char *, uint32_t); 476 477 typedef struct { 478 const nvme_field_info_t *chk_fields; 479 size_t chk_index; 480 nvme_err_t chk_field_range; 481 nvme_err_t chk_field_unsup; 482 nvme_err_t chk_field_unuse; 483 } nvme_field_check_t; 484 485 extern bool nvme_field_check_one(nvme_ctrl_t *, uint64_t, const char *, 486 const nvme_field_check_t *, uint32_t allow); 487 488 /* 489 * Misc. functions. 490 */ 491 extern const char *nvme_tporttostr(nvme_ctrl_transport_t); 492 extern nvme_ns_disc_level_t nvme_ns_state_to_disc_level(nvme_ns_state_t); 493 extern const char *nvme_nsleveltostr(nvme_ns_disc_level_t); 494 495 /* 496 * Version related information and functions. There are statically declared 497 * version structures in the library for use for internal comparisons. Note, we 498 * have attempted to avoid a general comparison function in the internal API so 499 * that way it's always clear what we're comparing to a version and can't 500 * reverse things. 501 */ 502 extern const nvme_version_t nvme_vers_1v0; 503 extern const nvme_version_t nvme_vers_1v1; 504 extern const nvme_version_t nvme_vers_1v2; 505 extern const nvme_version_t nvme_vers_1v3; 506 extern const nvme_version_t nvme_vers_1v4; 507 extern const nvme_version_t nvme_vers_2v0; 508 509 extern bool nvme_vers_ctrl_atleast(const nvme_ctrl_t *, const nvme_version_t *); 510 extern bool nvme_vers_ctrl_info_atleast(const nvme_ctrl_info_t *, 511 const nvme_version_t *); 512 extern bool nvme_vers_ns_info_atleast(const nvme_ns_info_t *, 513 const nvme_version_t *); 514 515 /* 516 * Vendor-specific information. 517 */ 518 typedef struct nvme_vsd_ident { 519 const char *nvdi_human; 520 bool nvdi_subsys; 521 uint16_t nvdi_vid; 522 uint16_t nvdi_did; 523 uint16_t nvdi_svid; 524 uint16_t nvdi_sdid; 525 } nvme_vsd_ident_t; 526 527 typedef struct nvme_vsd { 528 const nvme_vsd_ident_t *nvd_ident; 529 size_t nvd_nident; 530 const nvme_log_page_info_t *const *nvd_logs; 531 size_t nvd_nlogs; 532 const nvme_vuc_disc_t *nvd_vuc; 533 size_t nvd_nvuc; 534 } nvme_vsd_t; 535 536 extern const nvme_log_page_info_t ocp_log_smart; 537 extern const nvme_log_page_info_t ocp_log_errrec; 538 extern const nvme_log_page_info_t ocp_log_fwact; 539 extern const nvme_log_page_info_t ocp_log_lat; 540 extern const nvme_log_page_info_t ocp_log_devcap; 541 extern const nvme_log_page_info_t ocp_log_unsup; 542 extern const nvme_log_page_info_t ocp_log_telstr; 543 544 extern const nvme_vsd_t wdc_sn840; 545 extern const nvme_vsd_t wdc_sn65x; 546 extern const nvme_vsd_t wdc_sn861; 547 extern const nvme_vsd_t micron_7300; 548 extern const nvme_vsd_t micron_74x0; 549 extern const nvme_vsd_t micron_x500; 550 extern const nvme_vsd_t micron_9550; 551 extern const nvme_vsd_t intel_p5510; 552 extern const nvme_vsd_t solidigm_p5x20; 553 extern const nvme_vsd_t solidigm_ps10x0; 554 extern const nvme_vsd_t kioxia_cd8; 555 extern const nvme_vsd_t phison_x200; 556 557 extern void nvme_vendor_map_ctrl(nvme_ctrl_t *); 558 extern bool nvme_vendor_vuc_supported(nvme_ctrl_t *, const char *); 559 560 /* 561 * Internal formatting functions that probably could be external. 562 */ 563 #define NVME_NGUID_NAMELEN 33 564 #define NVME_EUI64_NAMELEN 17 565 566 extern int nvme_format_nguid(const uint8_t [16], char *, size_t); 567 extern int nvme_format_eui64(const uint8_t [8], char *, size_t); 568 569 #ifdef __cplusplus 570 } 571 #endif 572 573 #endif /* _LIBNVME_IMPL_H */ 574