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
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
|
/*
* 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
* Copyright 2022 Tintri by DDN, Inc. All rights reserved.
*/
#ifndef _NVMEADM_H
#define _NVMEADM_H
#include <stdio.h>
#include <libdevinfo.h>
#include <libnvme.h>
#include <nvme_common.h>
#include <nvme_reg.h>
#include <ofmt.h>
#include <fm/libtopo.h>
#ifdef __cplusplus
extern "C" {
#endif
extern int verbose;
extern int debug;
/* Common structures */
typedef struct nvme_process_arg nvme_process_arg_t;
typedef struct nvmeadm_feature nvmeadm_feature_t;
typedef struct nvmeadm_cmd nvmeadm_cmd_t;
typedef enum {
/*
* Indicates a command that is allowed to run on multiple controllers.
*/
NVMEADM_C_MULTI = 1 << 0,
/*
* Indicates a command that requires exclusive access to the device.
*/
NVMEADM_C_EXCL = 1 << 1,
/*
* Indicates a command that does not run on a controller and therefore
* processing should not assume this.
*/
NVMEADM_C_NOCTRL = 1 << 2
} nvmeadm_cmd_flags_t;
/*
* General command structure
*/
struct nvmeadm_cmd {
const char *c_name;
const char *c_desc;
const char *c_flagdesc;
const char *c_fielddesc;
int (*c_func)(const nvme_process_arg_t *);
void (*c_usage)(const char *);
void (*c_optparse)(nvme_process_arg_t *);
nvmeadm_cmd_flags_t c_flags;
};
/*
* This is used to represent information for getting and printing specific
* features.
*/
struct nvmeadm_feature {
uint8_t f_feature;
boolean_t (*f_get)(const nvme_process_arg_t *, const nvme_feat_disc_t *,
const nvmeadm_feature_t *);
void (*f_print)(uint32_t, void *, size_t, const nvme_identify_ctrl_t *,
const nvme_version_t *);
};
struct nvme_process_arg {
nvme_t *npa_nvme;
nvme_ctrl_t *npa_ctrl;
nvme_ns_t *npa_ns;
nvme_ctrl_info_t *npa_ctrl_info;
nvme_ns_info_t *npa_ns_info;
int npa_argc;
char **npa_argv;
char *npa_name;
const char *npa_ctrl_name;
boolean_t npa_excl;
uint32_t npa_cmdflags;
const nvmeadm_cmd_t *npa_cmd;
const nvme_identify_ctrl_t *npa_idctl;
const nvme_version_t *npa_version;
ofmt_handle_t npa_ofmt;
void *npa_cmd_arg;
};
/*
* Command-specific arguments
*/
typedef enum {
NVMEADM_LIST_DEFAULT,
NVMEADM_LIST_CTRL,
NVMEADM_LIST_LOC
} nvmeadm_list_mode_t;
typedef struct {
nvmeadm_list_mode_t list_mode;
topo_hdl_t *list_topo;
} nvmeadm_list_t;
typedef struct {
boolean_t nll_unimpl;
nvme_log_disc_scope_t nll_scope;
uint32_t nll_nprint;
int nll_nfilts;
char *const *nll_filts;
boolean_t *nll_used;
} nvmeadm_list_logs_t;
typedef struct {
boolean_t nf_unimpl;
uint32_t nf_nprint;
uint32_t nf_nfilts;
char *const *nf_filts;
boolean_t *nf_used;
} nvmeadm_features_t;
typedef struct {
boolean_t ncn_use_flbas;
nvme_csi_t ncn_csi;
uint64_t ncn_size;
uint64_t ncn_cap;
uint32_t ncn_lba;
uint32_t ncn_nmic;
} nvmeadm_create_ns_t;
typedef struct {
const char *ngl_output;
bool ngl_hex;
} nvmeadm_get_logpage_t;
/* Version checking */
extern boolean_t nvme_version_check(const nvme_process_arg_t *,
const nvme_version_t *);
/* printing functions */
extern int nvme_strlen(const char *, int);
extern void nvme_print(int, const char *, int, const char *, ...);
extern int nvme_snprint_uint128(char *, size_t, nvme_uint128_t, int, int);
extern void nvme_print_ctrl_summary(nvme_ctrl_info_t *);
extern void nvme_print_nsid_summary(nvme_ns_info_t *);
extern void nvme_print_identify_ctrl(const nvme_identify_ctrl_t *, uint32_t,
const nvme_version_t *);
extern void nvme_print_identify_nsid(const nvme_identify_nsid_t *,
const nvme_version_t *);
extern void nvme_print_identify_nsid_list(const char *,
const nvme_identify_nsid_list_t *);
extern void nvme_print_identify_nsid_desc(void *);
extern void nvme_print_identify_ctrl_list(const char *,
const nvme_identify_ctrl_list_t *);
extern void nvme_print_error_log(int, const nvme_error_log_entry_t *,
const nvme_version_t *);
extern void nvme_print_health_log(const nvme_health_log_t *,
const nvme_identify_ctrl_t *,
const nvme_version_t *);
extern void nvme_print_fwslot_log(const nvme_fwslot_log_t *,
const nvme_identify_ctrl_t *);
extern void nvme_print_feat_unknown(nvme_feat_output_t, uint32_t, void *,
size_t);
extern void nvme_print_feat_arbitration(uint32_t, void *, size_t,
const nvme_identify_ctrl_t *, const nvme_version_t *);
extern void nvme_print_feat_power_mgmt(uint32_t, void *, size_t,
const nvme_identify_ctrl_t *, const nvme_version_t *);
extern void nvme_print_feat_lba_range(uint32_t, void *, size_t,
const nvme_identify_ctrl_t *, const nvme_version_t *);
extern void nvme_print_feat_temperature(uint32_t, void *, size_t,
const nvme_identify_ctrl_t *, const nvme_version_t *);
extern void nvme_print_feat_error(uint32_t, void *, size_t,
const nvme_identify_ctrl_t *, const nvme_version_t *);
extern void nvme_print_feat_write_cache(uint32_t, void *, size_t,
const nvme_identify_ctrl_t *, const nvme_version_t *);
extern void nvme_print_feat_nqueues(uint32_t, void *, size_t,
const nvme_identify_ctrl_t *, const nvme_version_t *);
extern void nvme_print_feat_intr_coal(uint32_t, void *, size_t,
const nvme_identify_ctrl_t *, const nvme_version_t *);
extern void nvme_print_feat_intr_vect(uint32_t, void *, size_t,
const nvme_identify_ctrl_t *, const nvme_version_t *);
extern void nvme_print_feat_write_atom(uint32_t, void *, size_t,
const nvme_identify_ctrl_t *, const nvme_version_t *);
extern void nvme_print_feat_async_event(uint32_t, void *, size_t,
const nvme_identify_ctrl_t *, const nvme_version_t *);
extern void nvme_print_feat_auto_pst(uint32_t, void *, size_t,
const nvme_identify_ctrl_t *, const nvme_version_t *);
extern void nvme_print_feat_progress(uint32_t, void *, size_t,
const nvme_identify_ctrl_t *, const nvme_version_t *);
extern void nvme_print_feat_host_behavior(uint32_t, void *, size_t,
const nvme_identify_ctrl_t *, const nvme_version_t *);
extern void nvmeadm_dump_hex(const uint8_t *, size_t);
/*
* ofmt related
*/
typedef struct {
const char *nloa_name;
di_node_t nloa_dip;
nvme_ctrl_info_t *nloa_ctrl;
nvme_ns_info_t *nloa_ns;
const char *nloa_disk;
const char *nloa_state;
char *nloa_loc;
char *nloa_ap;
} nvmeadm_list_ofmt_arg_t;
extern const ofmt_field_t nvmeadm_list_ctrl_ofmt[];
extern const ofmt_field_t nvmeadm_list_loc_ofmt[];
extern const ofmt_field_t nvmeadm_list_nsid_ofmt[];
typedef struct {
const char *nlloa_name;
const nvme_log_disc_t *nlloa_disc;
} nvmeadm_list_logs_ofmt_arg_t;
extern const char *nvmeadm_list_logs_fields;
extern const char *nvmeadm_list_logs_fields_impl;
extern const ofmt_field_t nvmeadm_list_logs_ofmt[];
typedef struct {
const char *nlfoa_name;
const nvme_feat_disc_t *nlfoa_feat;
} nvmeadm_list_features_ofmt_arg_t;
extern const char *nvmeadm_list_features_fields;
extern const ofmt_field_t nvmeadm_list_features_ofmt[];
/*
* Log pages that have special handling.
*/
extern int do_get_logpage_telemetry(const nvme_process_arg_t *,
const nvme_log_disc_t *, nvme_log_req_t *);
/*
* Warning and error cases. The default nvmeadm ones assume a libnvme related
* issue. Most errors are on the nvme_ctrl_t, which are the versions without any
* args. The ones that operate on the nvme_t handle have hdl in the name.
*/
extern void nvmeadm_warn(const nvme_process_arg_t *, const char *,
...) __PRINTFLIKE(2);
extern void nvmeadm_fatal(const nvme_process_arg_t *, const char *,
...) __PRINTFLIKE(2) __NORETURN;
extern void nvmeadm_hdl_warn(const nvme_process_arg_t *, const char *,
...) __PRINTFLIKE(2);
extern void nvmeadm_hdl_fatal(const nvme_process_arg_t *, const char *,
...) __PRINTFLIKE(2) __NORETURN;
/*
* Namespace Management Commands
*/
extern int do_create_ns(const nvme_process_arg_t *);
extern void optparse_create_ns(nvme_process_arg_t *);
extern void usage_create_ns(const char *);
extern int do_delete_ns(const nvme_process_arg_t *);
extern void usage_delete_ns(const char *);
extern int do_attach_ns(const nvme_process_arg_t *);
extern void usage_attach_ns(const char *);
extern int do_detach_ns(const nvme_process_arg_t *);
extern void usage_detach_ns(const char *);
/*
* Physical Eye Commands
*/
extern int do_measure_phyeye_cmd(const nvme_process_arg_t *);
extern void optparse_measure_phyeye_cmd(nvme_process_arg_t *);
extern void usage_measure_phyeye_cmd(const char *);
extern int do_report_phyeye_cmd(const nvme_process_arg_t *);
extern void optparse_report_phyeye_cmd(nvme_process_arg_t *);
extern void usage_report_phyeye_cmd(const char *);
/*
* Locking functions
*/
extern void nvmeadm_excl(const nvme_process_arg_t *, nvme_lock_level_t);
/*
* Vendor specific commands.
*
* All vendor commands must first call nvmeadm_vuc_validate() which will
* validate that a given vendor unique command is useable by the device and then
* proceed to take any necessary locks that the command suggests.
*/
extern nvme_vuc_disc_t *nvmeadm_vuc_init(const nvme_process_arg_t *,
const char *);
extern void nvmeadm_vuc_fini(const nvme_process_arg_t *, nvme_vuc_disc_t *);
extern int do_vendor_cmd(const nvme_process_arg_t *);
extern void optparse_vendor_cmd(nvme_process_arg_t *);
extern void usage_vendor_cmd(const char *);
extern int do_wdc_e6dump(const nvme_process_arg_t *);
extern void optparse_wdc_e6dump(nvme_process_arg_t *);
extern void usage_wdc_e6dump(const char *);
extern int do_wdc_resize(const nvme_process_arg_t *);
extern void optparse_wdc_resize(nvme_process_arg_t *);
extern void usage_wdc_resize(const char *);
extern int do_wdc_clear_assert(const nvme_process_arg_t *);
extern void usage_wdc_clear_assert(const char *);
extern int do_wdc_inject_assert(const nvme_process_arg_t *);
extern void usage_wdc_inject_assert(const char *);
extern int do_sandisk_hwrev(const nvme_process_arg_t *);
extern void usage_sandisk_hwrev(const char *);
extern int do_sandisk_pcieye(const nvme_process_arg_t *);
extern void optparse_sandisk_pcieye(nvme_process_arg_t *);
extern void usage_sandisk_pcieye(const char *);
/*
* This is an arbitrary maximum that we use for what we expect the likely size
* of a log page may end up being. We use 128 MiB as a rough upper bound for
* what we'll mmap. This is a somewhat arbitrary value, but if we end up having
* a larger file, then we'll want to be more conscious of memory and probably
* read in a file in a buffer over time instead of mmap.
*/
#define NVMEADM_MAX_MMAP (1ULL << 27)
/*
* Field slicing and dicing. This is logic that is similar to pcieadm's
* show-cfgspace which allows us to select specific fields based on the short
* name of the log from the spec.
*/
typedef struct {
const char *nff_str;
size_t nff_len;
bool nff_used;
} nvmeadm_field_filt_t;
typedef enum nvmeadm_field_type_t {
/*
* Print as a raw hexadecimal value. The optional addend may be set to
* modify the value.
*/
NVMEADM_FT_HEX,
/*
* A number that should take the same shift as above, but have a
* particular unscaled unit applied.
*/
NVMEADM_FT_UNIT,
/*
* The raw value maps to a string of some kind.
*/
NVMEADM_FT_STRMAP,
/*
* Treat as a power of 2 number of bytes. Raw value is the full hex
* value. Otherwise this should be humanized.
*/
NVMEADM_FT_BYTES,
/*
* Indicates that this is a nested structure with a series of bitfields
* that we should print.
*/
NVMEADM_FT_BITS,
/*
* Indicate that there are a series of fields inside of this. Similar in
* spirit to BITS above, but generally meant to be used to help separate
* stuff which can be a little weirder such as the OCP telemetry string
* log or the various extended SMART items. Containers are only included
* in human readable output and are not part of the machine parsable
* output as they have no value.
*/
NVMEADM_FT_CONTAINER,
/*
* Indicates that this field is a normalized percentage. Note, this may
* result in a value > 100%.
*/
NVMEADM_FT_PERCENT,
/*
* A 16-byte style UUID.
*/
NVMEADM_FT_GUID,
/*
* A series of characters that are supposed to be ASCII strings. The
* ASCIIZ says that these are NUL padded where as ASCII
* is probably space padded. Either way padding is not guaranteed.
*/
NVMEADM_FT_ASCII,
NVMEADM_FT_ASCIIZ
} nvmeadm_field_type_t;
typedef struct {
uint8_t nfa_shift;
int64_t nfa_addend;
const char *nfa_unit;
} nvmeadm_field_addend_t;
typedef struct nvmeadm_field_bit nvmeadm_field_bit_t;
struct nvmeadm_field_bit {
uint8_t nfb_lowbit;
uint8_t nfb_hibit;
const char *nfb_short;
const char *nfb_desc;
uint8_t nfb_rev;
uint8_t nfb_maxrev;
const nvme_version_t *nfb_vers;
nvmeadm_field_type_t nfb_type;
/*
* Enough space for up to an 8-bit fields worth of values
* (though we expect most to be sparse).
*/
const char *nfb_strs[128];
const nvmeadm_field_bit_t *nfb_bits;
size_t nfb_nbits;
nvmeadm_field_addend_t nfb_addend;
};
typedef struct nvmeadm_field nvmeadm_field_t;
struct nvmeadm_field {
uint32_t nf_off;
uint32_t nf_len;
const char *nf_short;
const char *nf_desc;
uint32_t nf_rev;
uint32_t nf_maxrev;
const nvme_version_t *nf_vers;
nvmeadm_field_type_t nf_type;
/*
* Enough space for up to an 8-bit fields worth of values
* (though we expect most to be sparse).
*/
const char *nf_strs[128];
const nvmeadm_field_bit_t *nf_bits;
size_t nf_nbits;
nvmeadm_field_addend_t nf_addend;
const nvmeadm_field_t *nf_fields;
size_t nf_nfields;
};
typedef struct nvmeadm_field_print {
/*
* fp_header provides a header when printing this data. In general,
* 'fp_header' should only be used if we are breaking up a single log
* page or similar into multiple disjoint tables. This is used when
* there's a header for a log page and then a variable set of body
* entries (e.g. the PHY Eye Measurement).
*
* This header is paired with a 'base' string that corresponds to the
* short name for this region of the file. This should be set in fp_base
* and is required if fp_header is set.
*/
const char *fp_header;
/*
* Optional field revision and NVMe version information. If this is
* present, the field will be skipped if the object revision or the
* controller version is not sufficient. If this is against a file then
* the version checks are ignored by default.
*/
uint32_t fp_rev;
const nvme_version_t *fp_vers;
/*
* These are the set of fields to actually print.
*/
const nvmeadm_field_t *fp_fields;
size_t fp_nfields;
/*
* This represents the initial portion of the 'short' path. This will be
* prepended to all the different fields in here. This may be NULL if
* there is nothing here. When it is NULL, the header should be as well.
*/
const char *fp_base;
/*
* The data pointer and its corresponding length of valid data. Note,
* fp_off is a logical offset to be added. There is no relationship
* assumed between the data pointer and the fp_off. When a field is
* processed its embedded offset is always relative to the start of
* data. fp_off exists when manually driving so when a data pointer
* points to the start of some region that is offset, e.g. a pointer to
* the start of some variable length data after a header, then the
* logical offset in the overall structure can still be applied when
* telling the user about offsets.
*/
const void *fp_data;
size_t fp_dlen;
size_t fp_off;
/*
* Filters that are checked against.
*/
size_t fp_nfilts;
nvmeadm_field_filt_t *fp_filts;
ofmt_handle_t fp_ofmt;
/*
* Internal data used for indentation purposes.
*/
uint32_t fp_indent;
} nvmeadm_field_print_t;
/*
* Functions and data to reach the field printing logic.
*/
extern const ofmt_field_t nvmeadm_field_ofmt[];
extern void nvmeadm_field_print(nvmeadm_field_print_t *);
/*
* This is the internal function used by log processing code to reach the
* filtering / ofmt log formatting logic.
*/
typedef enum {
/*
* Indicates that this is the first time that a log page name is being
* checked. If it isn't found, then we should warn about it. This'll
* result in the command failing. This is mean to be used by
* print-logpage.
*/
NVMEADM_LFF_CHECK_NAME = 1 << 0
} nvmeadm_log_field_flag_t;
extern bool nvmeadm_log_page_fields(const nvme_process_arg_t *, const char *,
const void *, size_t, nvmeadm_field_filt_t *, size_t,
nvmeadm_log_field_flag_t);
/*
* Convenience macros to set a field type and consistent members. This should be
* used in the implementation of field information.
*/
#define NVMEADM_F_BITS(bits) \
.nf_type = NVMEADM_FT_BITS, \
.nf_bits = bits, \
.nf_nbits = ARRAY_SIZE(bits)
#define NVMEADM_FB_BITS(bits) \
.nfb_type = NVMEADM_FT_BITS, \
.nfb_bits = bits, \
.nfb_nbits = ARRAY_SIZE(bits)
#define NVMEADM_F_FIELDS(f) \
.nf_type = NVMEADM_FT_CONTAINER, \
.nf_fields = f, \
.nf_nfields = ARRAY_SIZE(f)
/*
* Defined field structures.
*/
typedef struct {
const char *nlfi_log;
const nvmeadm_field_t *const nlfi_fields;
const size_t nlfi_nfields;
const size_t nlfi_min;
/*
* Return the revision of the log field. Callers are guaranteed that at
* least nlfi_min byte are already present,
*/
uint32_t (*nlfi_getrev)(const void *, size_t len);
/*
* Run the process of walking through the log data and providing field
* callbacks. This should be used for logs that have a fixed header and
* variable contents.
*/
bool (*nlfi_drive)(nvmeadm_field_print_t *, const void *, size_t);
} nvmeadm_log_field_info_t;
extern const nvmeadm_log_field_info_t suplog_field_info;
extern const nvmeadm_log_field_info_t supcmd_field_info;
extern const nvmeadm_log_field_info_t supmicmd_field_info;
extern const nvmeadm_log_field_info_t supfeat_field_info;
extern const nvmeadm_log_field_info_t phyeye_field_info;
extern const nvmeadm_log_field_info_t kioxia_vul_extsmart_field_info;
extern const nvmeadm_log_field_info_t micron_vul_extsmart_field_info;
extern const nvmeadm_log_field_info_t ocp_vul_smart_field_info;
extern const nvmeadm_log_field_info_t ocp_vul_errrec_field_info;
extern const nvmeadm_log_field_info_t ocp_vul_devcap_field_info;
extern const nvmeadm_log_field_info_t ocp_vul_unsup_field_info;
extern const nvmeadm_log_field_info_t ocp_vul_telstr_field_info;
extern const nvmeadm_log_field_info_t solidigm_vul_power_field_info;
extern const nvmeadm_log_field_info_t solidigm_vul_temp_field_info;
extern const nvmeadm_log_field_info_t wdc_vul_cusmart_field_info;
extern const nvmeadm_log_field_info_t wdc_vul_eol_field_info;
extern const nvmeadm_log_field_info_t wdc_vul_power_field_info;
#ifdef __cplusplus
}
#endif
#endif /* _NVMEADM_H */
|