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 * WDC e6 request. This was made an opaque request style structure to try to 377 * safeguard us against future changes where something like the optional mode 378 * byte was required (right now it's just always zero). 379 */ 380 struct nvme_wdc_e6_req { 381 uint32_t wer_need; 382 nvme_vuc_req_t *wer_vuc; 383 }; 384 385 /* 386 * Common interfaces for operation success and failure. There are currently 387 * errors that can exist on four different objects in the library and there is 388 * one success() and error() function for each of them. See the theory statement 389 * section on errors in libnvme.c for more information. Note, all namespace and 390 * request structures set errors on the controller. 391 * 392 * The controller has an extra error path that is used for converting ioctls to 393 * semantic errors. It takes care of translating the different kinds of kernel 394 * errors to the library's errors. Our goal is to never programmatically leak 395 * the kernel ioctls and their error codes as they do not promise stability 396 * unlike our aspirations. It also doesn't allow for variable arguments and only 397 * takes a single description. 398 */ 399 extern bool nvme_error(nvme_t *, nvme_err_t, int32_t, const char *, 400 ...) __PRINTFLIKE(4); 401 extern bool nvme_success(nvme_t *); 402 403 extern bool nvme_ctrl_error(nvme_ctrl_t *, nvme_err_t, int32_t, const char *, 404 ...) __PRINTFLIKE(4); 405 extern bool nvme_ioctl_error(nvme_ctrl_t *, const nvme_ioctl_common_t *, 406 const char *); 407 extern bool nvme_ioctl_syserror(nvme_ctrl_t *, int, const char *); 408 extern bool nvme_ctrl_success(nvme_ctrl_t *); 409 410 extern bool nvme_info_error(nvme_ctrl_info_t *, nvme_info_err_t, int32_t, 411 const char *, ...) __PRINTFLIKE(4); 412 extern bool nvme_info_success(nvme_ctrl_info_t *); 413 414 extern bool nvme_ns_info_error(nvme_ns_info_t *, nvme_info_err_t, int32_t, 415 const char *, ...) __PRINTFLIKE(4); 416 extern bool nvme_ns_info_success(nvme_ns_info_t *); 417 418 /* 419 * Common functions for preserving and restoring error data. This comes up when 420 * utilizing callback functions for discovery where we call libnvme functions. 421 */ 422 extern void nvme_err_save(const nvme_t *, nvme_err_data_t *); 423 extern void nvme_err_set(nvme_t *, const nvme_err_data_t *); 424 extern void nvme_ctrl_err_save(const nvme_ctrl_t *, nvme_err_data_t *); 425 extern void nvme_ctrl_err_set(nvme_ctrl_t *, const nvme_err_data_t *); 426 427 /* 428 * Common functions for issuing ioctls to a controller. 429 */ 430 extern bool nvme_ioc_ctrl_info(nvme_ctrl_t *, nvme_ioctl_ctrl_info_t *); 431 extern bool nvme_ioc_ns_info(nvme_ctrl_t *, uint32_t, nvme_ioctl_ns_info_t *); 432 433 /* 434 * Common validation template functions. 435 */ 436 extern bool nvme_field_miss_err(nvme_ctrl_t *, const nvme_field_info_t *, 437 size_t, nvme_err_t, const char *, uint32_t); 438 439 typedef struct { 440 const nvme_field_info_t *chk_fields; 441 size_t chk_index; 442 nvme_err_t chk_field_range; 443 nvme_err_t chk_field_unsup; 444 nvme_err_t chk_field_unuse; 445 } nvme_field_check_t; 446 447 extern bool nvme_field_check_one(nvme_ctrl_t *, uint64_t, const char *, 448 const nvme_field_check_t *, uint32_t allow); 449 450 /* 451 * Misc. functions. 452 */ 453 extern const char *nvme_tporttostr(nvme_ctrl_transport_t); 454 extern nvme_ns_disc_level_t nvme_ns_state_to_disc_level(nvme_ns_state_t); 455 extern const char *nvme_nsleveltostr(nvme_ns_disc_level_t); 456 457 /* 458 * Version related information and functions. There are statically declared 459 * version structures in the library for use for internal comparisons. Note, we 460 * have attempted to avoid a general comparison function in the internal API so 461 * that way it's always clear what we're comparing to a version and can't 462 * reverse things. 463 */ 464 extern const nvme_version_t nvme_vers_1v0; 465 extern const nvme_version_t nvme_vers_1v1; 466 extern const nvme_version_t nvme_vers_1v2; 467 extern const nvme_version_t nvme_vers_1v3; 468 extern const nvme_version_t nvme_vers_1v4; 469 extern const nvme_version_t nvme_vers_2v0; 470 471 extern bool nvme_vers_ctrl_atleast(const nvme_ctrl_t *, const nvme_version_t *); 472 extern bool nvme_vers_ctrl_info_atleast(const nvme_ctrl_info_t *, 473 const nvme_version_t *); 474 extern bool nvme_vers_ns_info_atleast(const nvme_ns_info_t *, 475 const nvme_version_t *); 476 477 /* 478 * Vendor-specific information. 479 */ 480 typedef struct nvme_vsd_ident { 481 const char *nvdi_human; 482 bool nvdi_subsys; 483 uint16_t nvdi_vid; 484 uint16_t nvdi_did; 485 uint16_t nvdi_svid; 486 uint16_t nvdi_sdid; 487 } nvme_vsd_ident_t; 488 489 typedef struct nvme_vsd { 490 const nvme_vsd_ident_t *nvd_ident; 491 size_t nvd_nident; 492 const nvme_log_page_info_t *const *nvd_logs; 493 size_t nvd_nlogs; 494 const nvme_vuc_disc_t *nvd_vuc; 495 size_t nvd_nvuc; 496 } nvme_vsd_t; 497 498 extern const nvme_log_page_info_t ocp_log_smart; 499 extern const nvme_log_page_info_t ocp_log_errrec; 500 extern const nvme_log_page_info_t ocp_log_fwact; 501 extern const nvme_log_page_info_t ocp_log_lat; 502 extern const nvme_log_page_info_t ocp_log_devcap; 503 extern const nvme_log_page_info_t ocp_log_unsup; 504 extern const nvme_log_page_info_t ocp_log_telstr; 505 506 extern const nvme_vsd_t wdc_sn840; 507 extern const nvme_vsd_t wdc_sn65x; 508 extern const nvme_vsd_t wdc_sn861; 509 extern const nvme_vsd_t micron_7300; 510 extern const nvme_vsd_t micron_74x0; 511 extern const nvme_vsd_t micron_x500; 512 extern const nvme_vsd_t micron_9550; 513 extern const nvme_vsd_t intel_p5510; 514 extern const nvme_vsd_t solidigm_p5x20; 515 extern const nvme_vsd_t solidigm_ps10x0; 516 extern const nvme_vsd_t kioxia_cd8; 517 extern const nvme_vsd_t phison_x200; 518 519 extern void nvme_vendor_map_ctrl(nvme_ctrl_t *); 520 extern bool nvme_vendor_vuc_supported(nvme_ctrl_t *, const char *); 521 522 /* 523 * Internal formatting functions that probably could be external. 524 */ 525 #define NVME_NGUID_NAMELEN 33 526 #define NVME_EUI64_NAMELEN 17 527 528 extern int nvme_format_nguid(const uint8_t [16], char *, size_t); 529 extern int nvme_format_eui64(const uint8_t [8], char *, size_t); 530 531 #ifdef __cplusplus 532 } 533 #endif 534 535 #endif /* _LIBNVME_IMPL_H */ 536