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