xref: /illumos-gate/usr/src/common/nvme/nvme_common.h (revision ffd3f1847d39a0d148aa934d9051f453189adfe2)
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 _NVME_COMMON_H
17 #define	_NVME_COMMON_H
18 
19 /*
20  * Collection of common files and utilities that can be used for NVMe related
21  * functionality. Broadly, these are meant so that the kernel and userland have
22  * consistent validation routines.
23  *
24  * When we perform error checking and validation we use the kernel's set of
25  * ioctl errors for more semantic errors. These semantic errors are translated
26  * into ones that the library wishes to expose. Our goal is to try to use a
27  * mostly uniform error checking framework between the two entities.
28  *
29  * A consumer must build nvme_version.o and nvme_field.o. Other pieces can be
30  * added based on their needs.
31  */
32 
33 #include <sys/stdbool.h>
34 #include <sys/nvme.h>
35 #include <sys/nvme/discovery.h>
36 
37 #ifdef __cplusplus
38 extern "C" {
39 #endif
40 
41 /*
42  * Version related pieces from nvme_version.c. The main idea is that consumers
43  * such as the kernel and libnvme will wrap up the nvme_vers_atleast() function
44  * with an object that contains an NVMe version, thus reducing the likelihood
45  * that we'll confuse versions.
46  */
47 extern const nvme_version_t nvme_vers_1v0;
48 extern const nvme_version_t nvme_vers_1v1;
49 extern const nvme_version_t nvme_vers_1v2;
50 extern const nvme_version_t nvme_vers_1v3;
51 extern const nvme_version_t nvme_vers_1v4;
52 extern const nvme_version_t nvme_vers_2v0;
53 extern const nvme_version_t nvme_vers_2v1;
54 
55 extern bool nvme_vers_atleast(const nvme_version_t *, const nvme_version_t *);
56 
57 /*
58  * This structure contains information about the controller that must be
59  * supplied to the various validation functions.
60  */
61 typedef struct nvme_valid_ctrl_data {
62 	const nvme_version_t *vcd_vers;
63 	const nvme_identify_ctrl_t *vcd_id;
64 } nvme_valid_ctrl_data_t;
65 
66 /*
67  * This structure is used to represent a field that is in use in a given
68  * command. This allows us to use common validation logic for different classes
69  * of commands such as IDENTIFY, GET LOG PAGE, etc. If everything is fine about
70  * a field, then it should return true. Otherwise, it should return false and
71  * fill out the error message. It is optional to override the specifics of the
72  * nvme_ioctl_err_t with a more specific error where appropriate and known. If
73  * it is not filled in, the validation default will be used.
74  */
75 struct nvme_field_info;
76 typedef bool (*nvme_field_sup_f)(const struct nvme_field_info *,
77     const nvme_valid_ctrl_data_t *, char *, size_t);
78 typedef bool (*nvme_field_valid_f)(const struct nvme_field_info *,
79     const nvme_valid_ctrl_data_t *, uint64_t, char *, size_t);
80 
81 typedef struct nvme_field_info {
82 	const nvme_version_t *nlfi_vers;
83 	nvme_field_sup_f nlfi_sup;
84 	uint64_t nlfi_max_size;
85 	nvme_field_valid_f nlfi_valid;
86 	/*
87 	 * Fields below this point are mostly meant to be used by libnvme and by
88 	 * our printing logic, which we assume is not executed in the kernel.
89 	 */
90 	const char *nlfi_spec;
91 	const char *nlfi_human;
92 	bool nlfi_def_req;
93 	bool nlfi_def_allow;
94 } nvme_field_info_t;
95 
96 typedef enum {
97 	NVME_FIELD_ERR_OK = 0,
98 	NVME_FIELD_ERR_UNSUP_VERSION,
99 	NVME_FIELD_ERR_UNSUP_FIELD,
100 	NVME_FIELD_ERR_BAD_VALUE
101 } nvme_field_error_t;
102 
103 extern nvme_field_error_t nvme_field_validate(const nvme_field_info_t *,
104     const nvme_valid_ctrl_data_t *, uint64_t, char *, size_t);
105 
106 /*
107  * Various common utility routines for field validation and implementation. This
108  * version of NSID checking treats the NSID as valid. Currently checking for the
109  * validity of the broadcast namespace ID is left to consumers.
110  */
111 extern bool nvme_field_atleast(const nvme_valid_ctrl_data_t *,
112     const nvme_version_t *);
113 extern bool nvme_field_valid_nsid(const nvme_field_info_t *,
114     const nvme_valid_ctrl_data_t *, uint64_t, char *, size_t);
115 extern bool nvme_field_range_check(const nvme_field_info_t *, uint64_t,
116     uint64_t, char *, size_t, uint64_t);
117 extern bool nvme_field_mask_check(const nvme_field_info_t *, uint64_t, char *,
118     size_t, uint64_t);
119 
120 /*
121  * Log page request information. The goal with these structures and fields is to
122  * be able to validate whether something is valid, both in user/kernel context.
123  * This phrasing also makes this much easier to unit test. Because information
124  * is shared between libnvme and the kernel, some things are not needed for the
125  * kernel. We do not ifdef it out for the moment, to simplify things.
126  */
127 
128 /*
129  * This is the set of fields that the driver knows about how to validate that
130  * can end up in an NVMe log request. Items should be added here once the kernel
131  * knows how to put them in a log request command.
132  */
133 typedef enum {
134 	NVME_LOG_REQ_FIELD_LID	= 0,
135 	NVME_LOG_REQ_FIELD_LSP,
136 	NVME_LOG_REQ_FIELD_LSI,
137 	NVME_LOG_REQ_FIELD_SIZE,
138 	NVME_LOG_REQ_FIELD_CSI,
139 	NVME_LOG_REQ_FIELD_RAE,
140 	NVME_LOG_REQ_FIELD_OFFSET,
141 	NVME_LOG_REQ_FIELD_NSID
142 } nvme_log_req_field_t;
143 
144 extern const nvme_field_info_t nvme_log_fields[];
145 extern const size_t nvme_log_nfields;
146 
147 /*
148  * We now use the field based information to have a common structure to define
149  * information about standard log pages.
150  */
151 typedef struct nvme_log_page_info nvme_log_page_info_t;
152 typedef bool (*nvme_log_page_sup_f)(const nvme_valid_ctrl_data_t *,
153     const nvme_log_page_info_t *);
154 typedef uint64_t (*nvme_log_page_len_f)(const nvme_valid_ctrl_data_t *,
155     const nvme_log_page_info_t *);
156 typedef nvme_log_disc_scope_t (*nvme_log_page_scope_f)(
157     const nvme_valid_ctrl_data_t *, const nvme_log_page_info_t *);
158 typedef bool (*nvme_log_page_var_len_f)(uint64_t *, const void *, size_t);
159 
160 struct nvme_log_page_info {
161 	const char *nlpi_short;
162 	const char *nlpi_human;
163 	const char *const *nlpi_aliases;
164 	size_t nlpi_naliases;
165 	uint32_t nlpi_lid;
166 	nvme_csi_t nlpi_csi;
167 	/*
168 	 * These two entries can be used to determine whether a log page is
169 	 * supported based upon its version or with a supplemental function. A
170 	 * NULL item means it doesn't need to be checked. This would be the case
171 	 * for vendor-specific logs.
172 	 */
173 	const nvme_version_t *nlpi_vers;
174 	const nvme_log_page_sup_f nlpi_sup_func;
175 	nvme_log_disc_kind_t nlpi_kind;
176 	nvme_log_disc_source_t nlpi_source;
177 	nvme_log_disc_fields_t nlpi_disc;
178 	/*
179 	 * Log pages are valid in certain contexts. This is generally static
180 	 * information, but if the scope function is implemented, we will use
181 	 * that and ignore the contents of nlpi_scope.
182 	 */
183 	nvme_log_disc_scope_t nlpi_scope;
184 	nvme_log_page_scope_f nlpi_scope_func;
185 	/*
186 	 * The lengths for a log page come in three forms. The first form is
187 	 * ones where we can determine based on information in the controller
188 	 * (or at build time) the length of the log page. Many log pages have a
189 	 * fixed length or they include information in the identify controller
190 	 * data structure as to their length (e.g. the error log page). To
191 	 * communicate the log page's length, we will first check if
192 	 * nlpi_len_func is non-NULL and call that to determine the log page
193 	 * length. Otherwise we will use the value in nlpi_len. If these return
194 	 * a non-zero value, the NVME_LOG_DISC_F_SIZE_FIXED will be set
195 	 * automatically.
196 	 *
197 	 * The second form of log pages are those whose length is variable, but
198 	 * we cannot determine it based on information present in the
199 	 * controller. Rather we must read some amount of data from the log page
200 	 * to figure this out at all. For example, many vendor specific logs
201 	 * have a first uint32_t that indicates the number of valid samples and
202 	 * therefore you must read that to determine the overall length of the
203 	 * log page. This case follows the same path as the first case; however,
204 	 * one must also set the nlpi_var_func function pointer. This results
205 	 * in the NVME_LOG_DISC_F_SIZE_VAR flag being set.
206 	 *
207 	 * The third set of these are ones we just don't know about. In this
208 	 * case, leave nlpi_len set to zero and nlpi_len_func to NULL. If this
209 	 * happens or neither path returns a valid size (i.e. 0) then we will
210 	 * set this to a general size that should be large enough (i.e. the
211 	 * non-extended NVMe log page size) and not set either size flag.
212 	 */
213 	uint64_t nlpi_len;
214 	nvme_log_page_len_f nlpi_len_func;
215 	nvme_log_page_var_len_f nlpi_var_func;
216 };
217 
218 extern const nvme_log_page_info_t nvme_std_log_pages[];
219 extern const size_t nvme_std_log_npages;
220 
221 /*
222  * These are functions that can be used to compute information about what's
223  * supported and similar information that sometimes requires dynamic support.
224  */
225 extern nvme_log_disc_scope_t nvme_log_page_info_scope(
226     const nvme_log_page_info_t *, const nvme_valid_ctrl_data_t *);
227 extern uint64_t nvme_log_page_info_size(const nvme_log_page_info_t *,
228     const nvme_valid_ctrl_data_t *, bool *);
229 extern bool nvme_log_page_info_supported(const nvme_log_page_info_t *,
230     const nvme_valid_ctrl_data_t *);
231 
232 /*
233  * This next section identifies the various fields that make up the NVMe
234  * IDENTIFY command and the corresponding pieces that are in use throughout.
235  */
236 typedef enum {
237 	NVME_ID_REQ_F_CNS = 0,
238 	NVME_ID_REQ_F_NSID,
239 	NVME_ID_REQ_F_CTRLID,
240 	NVME_ID_REQ_F_BUF,
241 } nvme_identify_req_field_t;
242 
243 typedef enum {
244 	/*
245 	 * Indicates that we allow this identify command to operate on a
246 	 * namespace minor.
247 	 */
248 	NVME_IDENTIFY_INFO_F_NS_OK		= 1 << 0,
249 	/*
250 	 * Indicates that if we support namespace management we should attempt
251 	 * to use the broadcast nsid when asking about the controller.
252 	 */
253 	NVME_IDENTIFY_INFO_F_BCAST		= 1 << 1,
254 	/*
255 	 * This indicates that we are performing an operation which lists
256 	 * namespace IDs. As such, we don't need to validate the namespace
257 	 * against the controller's list. In addition, a zero namespace ID is
258 	 * allowed.
259 	 */
260 	NVME_IDENTIFY_INFO_F_NSID_LIST		= 1 << 2
261 } nvme_identify_info_flags_t;
262 
263 typedef struct nvme_identify_info nvme_identify_info_t;
264 typedef bool (*nvme_identify_sup_f)(const nvme_valid_ctrl_data_t *);
265 struct nvme_identify_info {
266 	const char			*nii_name;
267 	nvme_csi_t			nii_csi;
268 	uint32_t			nii_cns;
269 	const nvme_version_t		*nii_vers;
270 	nvme_identify_sup_f		nii_sup_func;
271 	nvme_identify_req_field_t	nii_fields;
272 	nvme_identify_info_flags_t	nii_flags;
273 };
274 
275 extern const nvme_field_info_t nvme_identify_fields[];
276 extern const size_t nvme_identify_nfields;
277 extern const nvme_identify_info_t nvme_identify_cmds[];
278 extern const size_t nvme_identify_ncmds;
279 
280 extern bool nvme_identify_info_supported(const nvme_identify_info_t *,
281     const nvme_valid_ctrl_data_t *);
282 
283 /*
284  * NVMe Vendor Unique Commands. Note, unlike others this hasn't really changed
285  * since it was introduced in NVMe 1.0. While libnvme wraps these up a bit to
286  * construct commands, there is no common vendor unique command discovery
287  * information as the kernel more or less stays out of it.
288  */
289 typedef enum {
290 	NVME_VUC_REQ_FIELD_OPC = 0,
291 	NVME_VUC_REQ_FIELD_NSID,
292 	NVME_VUC_REQ_FIELD_CDW12,
293 	NVME_VUC_REQ_FIELD_CDW13,
294 	NVME_VUC_REQ_FIELD_CDW14,
295 	NVME_VUC_REQ_FIELD_CDW15,
296 	NVME_VUC_REQ_FIELD_NDT,
297 	/*
298 	 * While the timeout field here is not actually part of the standard, we
299 	 * require it as part of the command execution and therefore include it
300 	 * in here.
301 	 */
302 	NVME_VUC_REQ_FIELD_TO
303 } nvme_vuc_req_field_t;
304 
305 extern const nvme_field_info_t nvme_vuc_fields[];
306 extern const size_t nvme_vuc_nfields;
307 
308 /*
309  * Firmware download and commit related fields and routines.
310  */
311 typedef enum {
312 	NVME_FW_LOAD_REQ_FIELD_NUMD = 0,
313 	NVME_FW_LOAD_REQ_FIELD_OFFSET
314 } nvme_fw_load_req_field_t;
315 
316 extern const nvme_field_info_t nvme_fw_load_fields[];
317 extern const size_t nvme_fw_load_nfields;
318 
319 extern bool nvme_fw_cmds_supported(const nvme_valid_ctrl_data_t *);
320 extern uint32_t nvme_fw_load_granularity(const nvme_valid_ctrl_data_t *);
321 
322 typedef enum {
323 	NVME_FW_COMMIT_REQ_FIELD_SLOT = 0,
324 	NVME_FW_COMMIT_REQ_FIELD_ACT
325 } nvme_fw_commit_req_field_t;
326 
327 extern const nvme_field_info_t nvme_fw_commit_fields[];
328 extern const size_t nvme_fw_commit_nfields;
329 
330 /*
331  * Format NVM operations
332  */
333 typedef enum {
334 	NVME_FORMAT_REQ_FIELD_LBAF	= 0,
335 	NVME_FORMAT_REQ_FIELD_SES,
336 	NVME_FORMAT_REQ_FIELD_NSID
337 } nvme_format_req_field_t;
338 
339 extern const nvme_field_info_t nvme_format_fields[];
340 extern const size_t nvme_format_nfields;
341 
342 extern bool nvme_format_cmds_supported(const nvme_valid_ctrl_data_t *);
343 
344 /*
345  * Feature related requests
346  */
347 typedef enum {
348 	NVME_GET_FEAT_REQ_FIELD_FID		= 0,
349 	NVME_GET_FEAT_REQ_FIELD_SEL,
350 	NVME_GET_FEAT_REQ_FIELD_DPTR,
351 	NVME_GET_FEAT_REQ_FIELD_CDW11,
352 	NVME_GET_FEAT_REQ_FIELD_NSID
353 } nvme_get_feat_req_field_t;
354 
355 extern const nvme_field_info_t nvme_get_feat_fields[];
356 extern const size_t nvme_get_feat_nfields;
357 
358 /*
359  * Common feature information.
360  */
361 typedef struct nvme_feat_info nvme_feat_info_t;
362 typedef bool (*nvme_feat_sup_f)(const nvme_valid_ctrl_data_t *,
363     const nvme_feat_info_t *);
364 
365 struct nvme_feat_info {
366 	const char *nfeat_short;
367 	const char *nfeat_spec;
368 	uint32_t nfeat_fid;
369 	/*
370 	 * These three entries can be used to determine whether a feature is
371 	 * supported or not based upon its version or supplemental information.
372 	 */
373 	const nvme_version_t *nfeat_vers;
374 	const nvme_feat_sup_f nfeat_sup_func;
375 	nvme_feat_kind_t nfeat_kind;
376 	/*
377 	 * These describe whether the feature operates on namespaces or the
378 	 * controller and misc. flags and information about them.
379 	 */
380 	nvme_feat_scope_t nfeat_scope;
381 	nvme_feat_csi_t nfeat_csi;
382 	nvme_feat_flags_t nfeat_flags;
383 	/*
384 	 * These four entries describe what an NVMe device uses as input and
385 	 * output fields.
386 	 */
387 	nvme_get_feat_fields_t nfeat_in_get;
388 	nvme_set_feat_fields_t nfeat_in_set;
389 	nvme_feat_output_t nfeat_out_get;
390 	nvme_feat_output_t nfeat_out_set;
391 	/*
392 	 * Feature data size. This should be zero if the feature does not use a
393 	 * data payload. Right now we assume the get and set sizes are identical
394 	 * as that's how this normally works.
395 	 */
396 	uint64_t nfeat_len;
397 };
398 
399 extern const nvme_feat_info_t nvme_std_feats[];
400 extern const size_t nvme_std_nfeats;
401 
402 extern nvme_feat_impl_t nvme_feat_supported(const nvme_feat_info_t *,
403     const nvme_valid_ctrl_data_t *);
404 
405 /*
406  * Namespace Management and Namespace Attach Commands.
407  *
408  * These operations have their own sets of NVMe admin operations codes.
409  * Separately, they then each have a means of selecting what they operate on in
410  * dw10. Unlike other operations like Get Features or Get Log Page, these are
411  * broken into separate ioctls in the kernel. In libnvme, namespace attach has a
412  * single command, but namespace create and delete are treated separately.
413  */
414 
415 typedef enum {
416 	NVME_NS_CREATE_REQ_FIELD_CSI = 0,
417 	NVME_NS_CREATE_REQ_FIELD_NSZE,
418 	NVME_NS_CREATE_REQ_FIELD_NCAP,
419 	NVME_NS_CREATE_REQ_FIELD_FLBAS,
420 	NVME_NS_CREATE_REQ_FIELD_NMIC
421 } nvme_ns_create_req_field_t;
422 
423 typedef enum {
424 	NVME_NS_DELETE_REQ_FIELD_NSID = 0
425 } nvme_ns_delete_req_field_t;
426 
427 /*
428  * Strictly speaking some of these fields, such as the controller list, are
429  * specific to the type of sub-command put into the SEL field.
430  */
431 typedef enum {
432 	NVME_NS_ATTACH_REQ_FIELD_SEL = 0,
433 	NVME_NS_ATTACH_REQ_FIELD_NSID,
434 	NVME_NS_ATTACH_REQ_FIELD_DPTR
435 } nvme_ns_attach_req_field_t;
436 
437 extern bool nvme_nsmgmt_cmds_supported(const nvme_valid_ctrl_data_t *);
438 extern const nvme_field_info_t nvme_ns_attach_fields[];
439 extern const size_t nvme_ns_attach_nfields;
440 extern const nvme_field_info_t nvme_ns_create_fields[];
441 extern const size_t nvme_ns_create_nfields;
442 extern const nvme_field_info_t nvme_ns_delete_fields[];
443 extern const size_t nvme_ns_delete_nfields;
444 
445 /*
446  * Allowed and required fields by CSI.
447  */
448 extern const nvme_ns_create_req_field_t nvme_ns_create_fields_nvm_req[];
449 extern const size_t nvme_ns_create_fields_nvm_nreq;
450 extern const nvme_ns_create_req_field_t nvme_ns_create_fields_nvm_allow[];
451 extern const size_t nvme_ns_create_fields_nvm_nallow;
452 
453 #ifdef __cplusplus
454 }
455 #endif
456 
457 #endif /* _NVME_COMMON_H */
458