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