xref: /illumos-gate/usr/src/lib/libnvme/common/libnvme_impl.h (revision 2aa8db5932a99c01d32f2aea7dbbf15b4898169b)
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