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