Bug Summary

File:.build-ci/../plugins/micron/micron-nvme.c
Warning:line 2317, column 11
Potential leak of memory pointed to by 'strPDir'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-redhat-linux-gnu -O3 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name micron-nvme.c -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model static -mframe-pointer=none -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/__w/nvme-cli/nvme-cli/.build-ci -fcoverage-compilation-dir=/__w/nvme-cli/nvme-cli/.build-ci -resource-dir /usr/bin/../lib/clang/22 -include /__w/nvme-cli/nvme-cli/.build-ci/nvme-config.h -I nvme.p -I . -I .. -I src -I ../src -I ccan -I ../ccan -I libnvme/src -I ../libnvme/src -I /usr/include/json-c -D _FILE_OFFSET_BITS=64 -D _GNU_SOURCE -U NDEBUG -internal-isystem /usr/bin/../lib/clang/22/include -internal-isystem /usr/local/include -internal-isystem /usr/bin/../lib/gcc/x86_64-redhat-linux/16/../../../../x86_64-redhat-linux/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -std=gnu11 -ferror-limit 19 -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -fcolor-diagnostics -vectorize-loops -vectorize-slp -analyzer-opt-analyze-headers -analyzer-output=html -faddrsig -fdwarf2-cfi-asm -o /__w/nvme-cli/nvme-cli/.build-ci/scan-results/2026-10-24-125112-639-1 -x c ../plugins/micron/micron-nvme.c
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Copyright (c) Micron, Inc 2024.
4 *
5 * @file: micron-nvme.c
6 * @brief: This module contains all the constructs needed for micron nvme-cli plugin.
7 * @authors:Hanumanthu H <hanumanthuh@micron.com>
8 * Chaithanya Shoba <ashoba@micron.com>
9 * Sivaprasad Gutha <sivaprasadg@micron.com>
10 */
11
12#include <ctype.h>
13#include <errno(*__errno_location ()).h>
14#include <fcntl.h>
15#include <inttypes.h>
16#include <libgen.h>
17#include <limits.h>
18#include <stddef.h>
19#include <stdio.h>
20#include <stdlib.h>
21#include <string.h>
22#include <sys/stat.h>
23#include <sys/types.h>
24#include <time.h>
25#include <unistd.h>
26
27#include <libnvme.h>
28
29#include <ccan/array_size/array_size.h>
30#include <ccan/endian/endian.h>
31#include <ccan/minmax/minmax.h>
32#include <shared/archive-util.h>
33#include <shared/compiler-attributes-util.h>
34#include <shared/fs-util.h>
35#include <shared/string-util.h>
36#include <shared/uint128-util.h>
37#include <shared/unaligned-util.h>
38
39#include "field-parser.h"
40#include "global-ctx.h"
41#include "micron-utils.h"
42#include "nvme-cmds.h"
43#include "nvme-pci-ids.h"
44#include "nvme-print.h"
45#include "plugin.h"
46#include "src/cleanup.h"
47
48/* Supported Vendor specific feature ids */
49#define MICRON_FEATURE_CLEAR_PCI_CORRECTABLE_ERRORS0xC3 0xC3
50#define MICRON_FEATURE_CLEAR_FW_ACTIVATION_HISTORY0xC1 0xC1
51#define MICRON_FEATURE_TELEMETRY_CONTROL_OPTION0xCF 0xCF
52#define MICRON_FEATURE_SMBUS_OPTION0xD5 0xD5
53#define MICRON_FEATURE_OCP_ENHANCED_TELEMETRY0x16 0x16
54
55/* Micron Supported Customer ID*/
56#define MICRON_CUST_ID_GENERAL0x10 0x10
57#define MICRON_CUST_ID_GG0x16 0x16
58
59/* Supported Vendor specific log page sizes */
60#define C5_log_size(((452 + 16 * 1024) / 4) * 4096) (((452 + 16 * 1024) / 4) * 4096)
61#define C0_log_size512 512
62#define C2_log_size4096 4096
63#define D0_log_size512 512
64#define FB_log_size512 512
65#define E1_log_size256 256
66#define MaxLogChunk(16 * 1024) (16 * 1024)
67#define CommonChunkSize(16 * 4096) (16 * 4096)
68#define C6_log_size512 512
69#define C5_MicronWorkLoad_log_size256 256
70
71#define SensorCount8 8
72
73/* Plugin version major_number.minor_number.patch */
74static const char *__version_major = "3";
75static const char *__version_minor = "0";
76static const char *__version_patch = "0";
77
78/* --option of the commands which enable, disable or show a feature */
79enum micron_option {
80 MICRON_OPTION_DISABLE,
81 MICRON_OPTION_ENABLE,
82 MICRON_OPTION_STATUS,
83};
84
85static struct argconfig_opt_val micron_options[] = {
86 VAL_ENUM("enable", MICRON_OPTION_ENABLE){"enable", CFG_ENUM, .val.int_val = MICRON_OPTION_ENABLE},
87 VAL_ENUM("disable", MICRON_OPTION_DISABLE){"disable", CFG_ENUM, .val.int_val = MICRON_OPTION_DISABLE},
88 VAL_ENUM("status", MICRON_OPTION_STATUS){"status", CFG_ENUM, .val.int_val = MICRON_OPTION_STATUS},
89 VAL_END(){ ((void*)0) }
90};
91
92/* --command of the latency commands */
93enum micron_command {
94 MICRON_COMMAND_ALL,
95 MICRON_COMMAND_READ,
96 MICRON_COMMAND_WRITE,
97 MICRON_COMMAND_TRIM,
98};
99
100static struct argconfig_opt_val micron_commands[] = {
101 VAL_ENUM("all", MICRON_COMMAND_ALL){"all", CFG_ENUM, .val.int_val = MICRON_COMMAND_ALL},
102 VAL_ENUM("read", MICRON_COMMAND_READ){"read", CFG_ENUM, .val.int_val = MICRON_COMMAND_READ},
103 VAL_ENUM("write", MICRON_COMMAND_WRITE){"write", CFG_ENUM, .val.int_val = MICRON_COMMAND_WRITE},
104 VAL_ENUM("trim", MICRON_COMMAND_TRIM){"trim", CFG_ENUM, .val.int_val = MICRON_COMMAND_TRIM},
105 VAL_END(){ ((void*)0) }
106};
107
108/*
109 * supported models of micron plugin; new models should be added at the end
110 * before UNKNOWN_MODEL. Make sure M5410 is first in the list !
111 */
112enum eDriveModel {
113 M5410 = 0,
114 M51AX,
115 M51BX,
116 M51BY,
117 M51CY,
118 M51CX,
119 M5407,
120 M5411,
121 M6001,
122 M6003,
123 M6004,
124 UNKNOWN_MODEL
125};
126
127#define MICRON_VENDOR_ID0x1344 0x1344
128
129static unsigned short vendor_id;
130static unsigned short device_id;
131
132/* Additional log page IDs */
133#define NVME_LOG_PERSISTENT_EVENT0xD 0xD
134
135struct LogPageHeader_t {
136 unsigned char numDwordsInLogPageHeaderLo;
137 unsigned char logPageHeaderFormatVersion;
138 unsigned char logPageId;
139 unsigned char numDwordsInLogPageHeaderHi;
140 unsigned int numValidDwordsInPayload;
141 unsigned int numDwordsInEntireLogPage;
142};
143
144struct MICRON_WORKLOAD_LOG_HDR {
145 unsigned short usNumEntries;
146 unsigned short usVersion;
147 unsigned int uiLength;
148};
149
150static void WriteData(__u8 *data, __u32 len, const char *dir, const char *file, const char *msg)
151{
152 __cleanup_free__attribute__((cleanup(shr_freep))) char *tempFolder = NULL((void*)0);
153 FILE *fpOutFile = NULL((void*)0);
154 int ret;
155
156 if (dir)
157 ret = asprintf(&tempFolder, "%s/%s", dir, file);
158 else
159 ret = asprintf(&tempFolder, "%s", file);
160 if (ret < 0) {
161 nvme_show_error("Failed to allocate memory for temp folder path")nvme_show_message(1, "Failed to allocate memory for temp folder path"
)
;
162 return;
163 }
164 fpOutFile = fopen(tempFolder, "ab+");
165 if (fpOutFile) {
166 if (fwrite(data, 1, len, fpOutFile) != len)
167 nvme_show_error("Failed to write %s data to %s", msg, tempFolder)nvme_show_message(1, "Failed to write %s data to %s", msg, tempFolder
)
;
168 fclose(fpOutFile);
169 } else {
170 nvme_show_error("Failed to open %s file to write %s", tempFolder, msg)nvme_show_message(1, "Failed to open %s file to write %s", tempFolder
, msg)
;
171 }
172}
173
174static enum eDriveModel GetDriveModel(
175 struct libnvme_global_ctx *ctx,
176 struct libnvme_transport_handle *hdl)
177{
178 enum eDriveModel eModel = UNKNOWN_MODEL;
179 uint32_t vid = 0, did = 0;
180
181 nvme_get_pci_ids(ctx, hdl, &vid, &did, NULL((void*)0), NULL((void*)0), NULL((void*)0));
182 vendor_id = (unsigned short)vid;
183 device_id = (unsigned short)did;
184
185 if (vendor_id == MICRON_VENDOR_ID0x1344) {
186 switch (device_id) {
187 case 0x5196:
188 case 0x51A0:
189 case 0x51A1:
190 case 0x51A2:
191 eModel = M51AX;
192 break;
193 case 0x51B0:
194 case 0x51B1:
195 case 0x51B2:
196 eModel = M51BX;
197 break;
198 case 0x51B7:
199 case 0x51B8:
200 case 0x51B9:
201 eModel = M51BY;
202 break;
203 case 0x51BB:
204 case 0x51BD:
205 case 0x51BC:
206 case 0x51BE:
207 case 0x51BF:
208 case 0x51C8:
209 case 0x51C9:
210 case 0x51CA:
211 case 0x51CB:
212 case 0x51CC:
213 case 0x51CD:
214 case 0x51CE:
215 eModel = M51CY;
216 break;
217 case 0x51C0:
218 case 0x51C1:
219 case 0x51C2:
220 case 0x51C3:
221 case 0x51C4:
222 eModel = M51CX;
223 break;
224 case 0x5405:
225 case 0x5406:
226 case 0x5407:
227 eModel = M5407;
228 break;
229 case 0x5410:
230 eModel = M5410;
231 break;
232 case 0x5411:
233 eModel = M5411;
234 break;
235 case 0x6001:
236 eModel = M6001;
237 break;
238 case 0x6004:
239 eModel = M6004;
240 break;
241 case 0x6003:
242 eModel = M6003;
243 break;
244 default:
245 break;
246 }
247 }
248 return eModel;
249}
250
251/*
252 * is_safe_path - validate that a path string is safe for use as a filename.
253 *
254 * Rejects control characters (0x00-0x1F), characters invalid on Windows
255 * filesystems (<>"|?*), paths starting with '-' which could be
256 * misinterpreted as flags by tar/zip, and trailing backslashes which
257 * would escape the closing quote in Windows command-line argument parsing.
258 */
259static bool_Bool is_safe_path(const char *path)
260{
261 /* Lookup table: 1 = rejected character */
262 static const unsigned char rejected[256] = {
263 [0x01 ... 0x1F] = 1, /* control characters */
264 ['<'] = 1,
265 ['>'] = 1,
266 ['"'] = 1,
267 ['|'] = 1,
268 ['?'] = 1,
269 ['*'] = 1,
270 };
271 const unsigned char *p = (const unsigned char *)path;
272
273 if (!path || !*path)
274 return false0;
275
276 if (path[0] == '-')
277 return false0;
278
279 for (; *p; p++) {
280 if (rejected[*p])
281 return false0;
282 }
283
284 if (p > (const unsigned char *)path && p[-1] == '\\')
285 return false0;
286
287 return true1;
288}
289
290static int ZipAndRemoveDir(char *strDirName, char *strFileName)
291{
292 enum shr_archive_format fmt;
293 int err = 0;
294 int nRet;
295 struct stat sb;
296
297 if (strstr(strFileName, ".tar.gz")_Generic (0 ? (strFileName) : (void *) 1, const void *: (const
char *) (strstr (strFileName, ".tar.gz")), default: strstr (
strFileName, ".tar.gz"))
|| strstr(strFileName, ".tgz")_Generic (0 ? (strFileName) : (void *) 1, const void *: (const
char *) (strstr (strFileName, ".tgz")), default: strstr (strFileName
, ".tgz"))
)
298 fmt = SHR_ARCHIVE_TAR_GZ;
299 else
300 fmt = SHR_ARCHIVE_ZIP;
301
302 nRet = shr_archive_create_dir(strFileName, strDirName, strDirName, fmt);
303
304 /* check if log file is created, if not print error message */
305 if (nRet || (stat(strFileName, &sb) == -1)) {
306 err = -EINVAL22;
307 if (nRet == -ENOTSUP95)
308 nvme_show_error(nvme_show_message(1, "Failed to create log data package, nvme-cli was built without libarchive support!\n"
)
309 "Failed to create log data package, nvme-cli was built without libarchive support!\n")nvme_show_message(1, "Failed to create log data package, nvme-cli was built without libarchive support!\n"
)
;
310 else
311 nvme_show_error("Failed to create log data package!\n")nvme_show_message(1, "Failed to create log data package!\n");
312 }
313
314 if (shr_rmdir_recursive(strDirName) < 0)
315 nvme_show_error("Failed to remove temporary files!")nvme_show_message(1, "Failed to remove temporary files!");
316
317 return err;
318}
319
320static int SetupDebugDataDirectories(char *strSN, char *strFilePath,
321 char *strMainDirName, size_t mainDirSize,
322 char *strOSDirName, size_t osDirSize,
323 char *strCtrlDirName, size_t ctrlDirSize)
324{
325 int err = 0;
326 struct stat st;
327 char *fileLocation = NULL((void*)0);
328 char *fileName;
329 int length = 0;
330 int nIndex = 0;
331 char *strTemp = NULL((void*)0);
332 int j;
333 int k = 0;
334
335 if (strchr(strFilePath, '/')_Generic (0 ? (strFilePath) : (void *) 1, const void *: (const
char *) (strchr (strFilePath, '/')), default: strchr (strFilePath
, '/'))
) {
336 fileName = strrchr(strFilePath, '\\')_Generic (0 ? (strFilePath) : (void *) 1, const void *: (const
char *) (strrchr (strFilePath, '\\')), default: strrchr (strFilePath
, '\\'))
;
337 if (!fileName)
338 fileName = strrchr(strFilePath, '/')_Generic (0 ? (strFilePath) : (void *) 1, const void *: (const
char *) (strrchr (strFilePath, '/')), default: strrchr (strFilePath
, '/'))
;
339
340 if (fileName) {
341 if (!strcmp(fileName, "/")) {
342 err = -1;
343 goto exit_status;
344 }
345
346 while (strFilePath[nIndex] != '\0') {
347 if ('\\' == strFilePath[nIndex] && '\\' == strFilePath[nIndex + 1]) {
348 err = -1;
349 goto exit_status;
350 }
351 nIndex++;
352 }
353
354 length = (int)strlen(strFilePath) - (int)strlen(fileName);
355
356 if (fileName == strFilePath)
357 length = 1;
358
359 fileLocation = (char *)malloc(length + 1);
360 if (!fileLocation) {
361 err = -1;
362 goto exit_status;
363 }
364 memcpy(fileLocation, strFilePath, length);
365 fileLocation[length] = '\0';
366
367 while (fileLocation[k] != '\0') {
368 if (fileLocation[k] == '\\')
369 fileLocation[k] = '/';
370 k++;
371 }
372
373 length = (int)strlen(fileLocation);
374
375 if (':' == fileLocation[length - 1]) {
376 strTemp = realloc(fileLocation, length + 2);
377
378 if (!strTemp) {
379 free(fileLocation);
380 err = -1;
381 goto exit_status;
382 }
383 fileLocation = strTemp;
384 fileLocation[length] = '/';
385 fileLocation[length + 1] = '\0';
386 length++;
387 }
388
389 if (stat(fileLocation, &st)) {
390 free(fileLocation);
391 err = -1;
392 goto exit_status;
393 }
394 free(fileLocation);
395 } else {
396 err = -1;
397 goto exit_status;
398 }
399 }
400
401 snprintf(strMainDirName, mainDirSize, "%s", strSN);
402 nIndex = strlen(strMainDirName);
403
404 j = 1;
405 while (shr_mkdir(strMainDirName, 0700) < 0) {
406 if (errno(*__errno_location ()) != EEXIST17) {
407 err = -1;
408 goto exit_status;
409 }
410 strMainDirName[nIndex] = '\0';
411 snprintf(strMainDirName + nIndex, mainDirSize - nIndex, "-%d", j);
412 j++;
413 }
414
415 if (strOSDirName) {
416 snprintf(strOSDirName, osDirSize, "%s/%s", strMainDirName, "OS");
417 if (shr_mkdir(strOSDirName, 0700) < 0) {
418 rmdir(strMainDirName);
419 err = -1;
420 goto exit_status;
421 }
422 }
423 if (strCtrlDirName) {
424 snprintf(strCtrlDirName, ctrlDirSize, "%s/%s", strMainDirName, "Controller");
425 if (shr_mkdir(strCtrlDirName, 0700) < 0) {
426 if (strOSDirName)
427 rmdir(strOSDirName);
428 rmdir(strMainDirName);
429 err = -1;
430 }
431 }
432
433exit_status:
434 return err;
435}
436
437static int GetLogPageSize(struct libnvme_transport_handle *hdl, unsigned char ucLogID, int *nLogSize)
438{
439 int err = 0;
440 unsigned char pTmpBuf[CommonChunkSize(16 * 4096)] = { 0 };
441 struct LogPageHeader_t *pLogHeader = NULL((void*)0);
442
443 if (ucLogID == 0xC1 || ucLogID == 0xC2 || ucLogID == 0xC4) {
444 err = nvme_get_log_simple(hdl, ucLogID, pTmpBuf, CommonChunkSize(16 * 4096));
445 if (!err) {
446 pLogHeader = (struct LogPageHeader_t *) pTmpBuf;
447 struct LogPageHeader_t *pLogHeader1 = (struct LogPageHeader_t *) pLogHeader;
448 *nLogSize = (int)(pLogHeader1->numDwordsInEntireLogPage) * 4;
449 if (!pLogHeader1->logPageHeaderFormatVersion) {
450 nvme_show_error("Unsupported log page format version %d of log page : 0x%X",nvme_show_message(1, "Unsupported log page format version %d of log page : 0x%X"
, ucLogID, err)
451 ucLogID, err)nvme_show_message(1, "Unsupported log page format version %d of log page : 0x%X"
, ucLogID, err)
;
452 *nLogSize = 0;
453 err = -1;
454 }
455 } else {
456 nvme_show_error("Getting size of log page : 0x%X failed with %d (ignored)!",nvme_show_message(1, "Getting size of log page : 0x%X failed with %d (ignored)!"
, ucLogID, err)
457 ucLogID, err)nvme_show_message(1, "Getting size of log page : 0x%X failed with %d (ignored)!"
, ucLogID, err)
;
458 *nLogSize = 0;
459 }
460 }
461 return err;
462}
463
464static int NVMEGetLogPage(struct libnvme_transport_handle *hdl, unsigned char ucLogID, unsigned char *pBuffer, int nBuffSize,
465 int offset)
466{
467 int err = 0;
468 struct libnvme_passthru_cmd cmd = { 0 };
469 unsigned int uiNumDwords = (unsigned int)nBuffSize / sizeof(unsigned int);
470 unsigned int uiMaxChunk = uiNumDwords;
471 unsigned int uiNumChunks = 1;
472 unsigned int uiXferDwords = 0;
473 unsigned long long ullBytesRead = offset;
474 unsigned char *pTempPtr = pBuffer;
475 unsigned char ucOpCode = 0x02;
476
477 if (!ullBytesRead && (ucLogID == 0xE6 || ucLogID == 0xE7))
478 uiMaxChunk = 4096;
479 else if (uiMaxChunk > 16 * 1024)
480 uiMaxChunk = 16 * 1024;
481
482 if (ucLogID == 0xE9) {
483 uiMaxChunk = 0x1D8C;
484 ullBytesRead = offset;
485 }
486
487 uiNumChunks = uiNumDwords / uiMaxChunk;
488 if (uiNumDwords % uiMaxChunk > 0)
489 uiNumChunks += 1;
490
491 for (unsigned int i = 0; i < uiNumChunks; i++) {
492 memset(&cmd, 0, sizeof(cmd));
493 uiXferDwords = uiMaxChunk;
494 if (i == uiNumChunks - 1 && uiNumDwords % uiMaxChunk > 0)
495 uiXferDwords = uiNumDwords % uiMaxChunk;
496
497 cmd.opcode = ucOpCode;
498 cmd.cdw10 |= ucLogID;
499 cmd.cdw10 |= ((uiXferDwords - 1) & 0x0000FFFF) << 16;
500
501 if (ucLogID == 0x7 && offset == 0)
502 cmd.cdw10 |= 0x100;
503
504 if (!ullBytesRead && (ucLogID == 0xE6 || ucLogID == 0xE7))
505 cmd.cdw11 = 1;
506 if (ullBytesRead > 0 && !(ucLogID == 0xE6 || ucLogID == 0xE7)) {
507 unsigned long long ullOffset = ullBytesRead;
508
509 cmd.cdw12 = ullOffset & 0xFFFFFFFF;
510 cmd.cdw13 = (ullOffset >> 32) & 0xFFFFFFFF;
511 }
512
513 cmd.addr = (__u64) (uintptr_t) pTempPtr;
514 cmd.nsid = 0xFFFFFFFF;
515 cmd.data_len = uiXferDwords * 4;
516 err = libnvme_exec_admin_passthru(hdl, &cmd);
517 ullBytesRead += uiXferDwords * 4;
518 if (ucLogID == 0x07 || ucLogID == 0x08 || ucLogID == 0xE9)
519 pTempPtr = pBuffer + (ullBytesRead - offset);
520 else
521 pTempPtr = pBuffer + ullBytesRead;
522 }
523
524 return err;
525}
526
527static int NVMEResetLog(struct libnvme_transport_handle *hdl, unsigned char ucLogID, int nBufferSize,
528 long long llMaxSize)
529{
530 __cleanup_libnvme_free__attribute__((cleanup(libnvme_freep))) unsigned int *pBuffer = NULL((void*)0);
531 int err = 0;
532
533 pBuffer = (unsigned int *)libnvme_alloc(nBufferSize);
534 if (!pBuffer)
535 return err;
536
537 while (!err && llMaxSize > 0) {
538 err = NVMEGetLogPage(hdl, ucLogID, (unsigned char *)pBuffer, nBufferSize, 0);
539 if (err)
540 return err;
541
542 if (pBuffer[0] == 0xdeadbeef)
543 break;
544
545 llMaxSize = llMaxSize - nBufferSize;
546 }
547
548 return err;
549}
550
551static int GetCommonLogPage(struct libnvme_transport_handle *hdl, unsigned char ucLogID,
552 unsigned char **pBuffer, int nBuffSize)
553{
554 unsigned char *pTempPtr = NULL((void*)0);
555 int err = 0;
556
557 pTempPtr = (unsigned char *)libnvme_alloc(nBuffSize);
558 if (!pTempPtr) {
559 err = -ENOMEM12;
560 goto exit_status;
561 }
562 err = nvme_get_log_simple(hdl, ucLogID, pTempPtr, nBuffSize);
563 *pBuffer = pTempPtr;
564
565exit_status:
566 return err;
567}
568
569/*
570 * Plugin Commands
571 */
572static int micron_parse_options(struct libnvme_global_ctx **ctx,
573 struct libnvme_transport_handle **hdl, int argc,
574 char **argv, const char *desc,
575 struct argconfig_commandline_options *opts,
576 enum eDriveModel *modelp)
577{
578 int err = parse_and_open(ctx, hdl, argc, argv, desc, opts);
579
580 if (err)
581 return err;
582
583 if (modelp)
584 *modelp = GetDriveModel(*ctx, *hdl);
585
586 return 0;
587}
588
589static int micron_fw_commit(struct libnvme_transport_handle *hdl, int select)
590{
591 struct libnvme_passthru_cmd cmd = {
592 .opcode = nvme_admin_fw_commit,
593 .cdw10 = 8,
594 .cdw12 = select,
595 };
596
597 return libnvme_exec_admin_passthru(hdl, &cmd);
598}
599
600static int micron_selective_download(int argc, char **argv,
601 struct command *command, struct plugin *plugin)
602{
603 const char *desc =
604 "This performs a selective firmware download, which allows the user to\n"
605 "select which firmware binary to update for 9200 devices. This requires\n"
606 "a power cycle once the update completes. The options available are:\n\n"
607 "OOB - This updates the OOB and main firmware\n"
608 "EEP - This updates the eeprom and main firmware\n"
609 "ALL - This updates the eeprom, OOB, and main firmware";
610 const char *fw = "firmware file (required)";
611 const char *select = "FW Select (e.g., --select=ALL)";
612
613 __cleanup_nvme_transport_handle__attribute__((cleanup(cleanup_nvme_transport_handle))) struct libnvme_transport_handle *hdl = NULL((void*)0);
614 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
615
616 int selectNo, fw_fd, fw_size, err, offset = 0;
617 struct libnvme_passthru_cmd cmd;
618 int xfer = 4096;
619 struct stat sb;
620 __cleanup_libnvme_free__attribute__((cleanup(libnvme_freep))) void *fw_buf = NULL((void*)0);
621 unsigned char *fw_ptr;
622
623 struct argconfig_opt_val selects[] = {
624 VAL_ENUM("OOB", 18){"OOB", CFG_ENUM, .val.int_val = 18},
625 VAL_ENUM("EEP", 10){"EEP", CFG_ENUM, .val.int_val = 10},
626 VAL_ENUM("ALL", 26){"ALL", CFG_ENUM, .val.int_val = 26},
627 VAL_END(){ ((void*)0) }
628 };
629
630 struct config {
631 char *fw;
632 int select;
633 };
634
635 struct config cfg = {
636 .fw = "",
637 .select = 0,
638 };
639
640 OPT_ARGS(opts)struct argconfig_commandline_options opts[] = {
641 OPT_FILE("fw", 'f', &cfg.fw, fw){"fw", 'f', "FILE", CFG_FILE, _Generic(&cfg.fw, char ** :
(&cfg.fw), const char ** : (&cfg.fw)), ARGCONFIG_ARG_REQUIRED
, fw, 0, }
,
642 OPT_ENUM("select", 's', &cfg.select, select, selects){"select", 's', "NAME", CFG_ENUM, _Generic(&cfg.select, int
* : (&cfg.select)), ARGCONFIG_ARG_REQUIRED, select, 0, selects
, }
,
643 OPT_END(){ ((void*)0) }
644 };
645
646 err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts);
647 if (err)
648 return err;
649
650 if (!argconfig_given(opts, &cfg.select)) {
651 nvme_show_error("Invalid select flag")nvme_show_message(1, "Invalid select flag");
652 return -EINVAL22;
653 }
654 selectNo = cfg.select;
655
656 fw_fd = open(cfg.fw, O_RDONLY00);
657 if (fw_fd < 0) {
658 nvme_show_error("no firmware file provided")nvme_show_message(1, "no firmware file provided");
659 return -EINVAL22;
660 }
661
662 err = fstat(fw_fd, &sb);
663 if (err < 0) {
664 nvme_show_perror("fstat");
665 err = errno(*__errno_location ());
666 goto out;
667 }
668
669 fw_size = sb.st_size;
670 if (fw_size & 0x3) {
671 nvme_show_error("Invalid size:%d for f/w image", fw_size)nvme_show_message(1, "Invalid size:%d for f/w image", fw_size
)
;
672 err = EINVAL22;
673 goto out;
674 }
675
676 fw_buf = libnvme_alloc(fw_size);
677 if (!fw_buf) {
678 nvme_show_error("No memory for f/w size:%d", fw_size)nvme_show_message(1, "No memory for f/w size:%d", fw_size);
679 err = ENOMEM12;
680 goto out;
681 }
682 fw_ptr = fw_buf;
683
684 if (read(fw_fd, fw_buf, fw_size) != ((ssize_t) (fw_size))) {
685 err = errno(*__errno_location ());
686 goto out;
687 }
688
689 while (fw_size > 0) {
690 xfer = min(xfer, fw_size)({ typeof(xfer) _a = (xfer); typeof(fw_size) _b = (fw_size); do
{ } while (0); _a < _b ? _a : _b; })
;
691
692 err = nvme_init_fw_download(&cmd, fw_ptr, xfer, offset);
693 if (err) {
694 nvme_show_err(err, "fw-download");
695 goto out;
696 }
697 err = libnvme_exec_admin_passthru(hdl, &cmd);
698 if (err) {
699 nvme_show_err(err, "fw-download");
700 goto out;
701 }
702 fw_ptr += xfer;
703 fw_size -= xfer;
704 offset += xfer;
705 }
706
707 err = micron_fw_commit(hdl, selectNo);
708
709 if (err == 0x10B || err == 0x20B) {
710 err = 0;
711 nvme_show_result(nvme_show_message(0, "Update successful! Power cycle for changes to take effect"
)
712 "Update successful! Power cycle for changes to take effect")nvme_show_message(0, "Update successful! Power cycle for changes to take effect"
)
;
713 }
714
715out:
716 close(fw_fd);
717 return err;
718}
719
720static int micron_smbus_option(int argc, char **argv,
721 struct command *command, struct plugin *plugin)
722{
723 __u64 result = 0;
724 __u32 cdw11 = 0;
725 const char *desc = "Enable/Disable/Get status of SMBUS option on controller";
726 const char *option = "enable or disable or status";
727 const char *value =
728 "1 - hottest component temperature, 0 - composite temperature (default) for enable option, 0 (current), 1 (default), 2 (saved) for status options";
729
730 const char *save = "1 - persistent, 0 - non-persistent (default)";
731 int fid = MICRON_FEATURE_SMBUS_OPTION0xD5;
732 enum eDriveModel model = UNKNOWN_MODEL;
733 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
734 __cleanup_nvme_transport_handle__attribute__((cleanup(cleanup_nvme_transport_handle))) struct libnvme_transport_handle *hdl = NULL((void*)0);
735 int err = 0;
736
737 struct {
738 int option;
739 __u32 value;
740 __u32 save;
741 int status;
742 } opt = {
743 .option = MICRON_OPTION_DISABLE,
744 .value = 0,
745 .save = 0,
746 .status = 0,
747 };
748
749 OPT_ARGS(opts)struct argconfig_commandline_options opts[] = {
750 OPT_ENUM("option", 'O', &opt.option, option, micron_options){"option", 'O', "NAME", CFG_ENUM, _Generic(&opt.option, int
* : (&opt.option)), ARGCONFIG_ARG_REQUIRED, option, 0, micron_options
, }
,
751 OPT_UINT("value", 'V', &opt.value, value){"value", 'V', "NUM", CFG_POSITIVE, _Generic(&opt.value, uint32_t
* : (&opt.value)), ARGCONFIG_ARG_REQUIRED, value, 0, }
,
752 OPT_UINT("save", 's', &opt.save, save){"save", 's', "NUM", CFG_POSITIVE, _Generic(&opt.save, uint32_t
* : (&opt.save)), ARGCONFIG_ARG_REQUIRED, save, 0, }
,
753 OPT_END(){ ((void*)0) }
754 };
755
756 err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &model);
757 if (err)
758 return err;
759
760 if (model != M5407 && model != M5411 && model != M6003 && model != M6004) {
761 nvme_show_error("This option is not supported for specified drive")nvme_show_message(1, "This option is not supported for specified drive"
)
;
762 return -ENOTSUP95;
763 }
764
765 if (opt.option == MICRON_OPTION_ENABLE) {
766 cdw11 = opt.value << 1 | 1;
767 err = nvme_set_features_simple(hdl, 1, fid, opt.save, cdw11,
768 &result);
769 if (!err)
770 nvme_show_verbose_result("successfully enabled SMBus on drive")nvme_show_verbose_message("successfully enabled SMBus on drive"
)
;
771 else
772 nvme_show_error("Failed to enabled SMBus on drive")nvme_show_message(1, "Failed to enabled SMBus on drive");
773 } else if (opt.option == MICRON_OPTION_STATUS) {
774 err = nvme_get_features(hdl, 1, fid, opt.value, 0, 0, NULL((void*)0), 0, &result);
775 if (!err)
776 printf("SMBus status on the drive: %s (returns %s temperature)\n",
777 (result & 1) ? "enabled" : "disabled",
778 (result & 2) ? "hottest component" : "composite");
779 else
780 nvme_show_error("Failed to retrieve SMBus status on the drive")nvme_show_message(1, "Failed to retrieve SMBus status on the drive"
)
;
781 } else {
782 cdw11 = opt.value << 1 | 0;
783 err = nvme_set_features_simple(hdl, 1, fid, opt.save, cdw11,
784 &result);
785 if (!err)
786 nvme_show_verbose_result("Successfully disabled SMBus on drive")nvme_show_verbose_message("Successfully disabled SMBus on drive"
)
;
787 else
788 nvme_show_error("Failed to disable SMBus on drive")nvme_show_message(1, "Failed to disable SMBus on drive");
789 }
790
791 return err;
792}
793
794static int micron_temp_stats(int argc, char **argv, struct command *acmd,
795 struct plugin *plugin)
796{
797
798 struct nvme_smart_log smart_log;
799 int err = 0;
800 unsigned int temperature = 0, i = 0;
801 unsigned int tempSensors[SensorCount8] = { 0 };
802 const char *desc = "Retrieve Micron temperature info for the given device ";
803 nvme_print_flags_t format;
804 bool_Bool is_json = false0;
805 struct json_object *root;
806 struct json_object *logPages;
807 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
808 __cleanup_nvme_transport_handle__attribute__((cleanup(cleanup_nvme_transport_handle))) struct libnvme_transport_handle *hdl = NULL((void*)0);
809
810 OPT_ARGS(opts)struct argconfig_commandline_options opts[] = {
811 OPT_END(){ ((void*)0) }
812 };
813
814 err = parse_and_open(&ctx, &hdl, argc, argv, desc, opts);
815 if (err)
816 return err;
817
818 format = nvme_args.output_flags;
819 is_json = format == JSON;
820
821 err = nvme_get_log_smart(hdl, NVME_NSID_ALL, &smart_log);
822 if (!err) {
823 temperature = ((smart_log.temperature[1] << 8) | smart_log.temperature[0]);
824 temperature = temperature ? temperature - 273 : 0;
825 for (i = 0; i < SensorCount8; i++) {
826 tempSensors[i] = le16_to_cpu(smart_log.temp_sensor[i]);
827 tempSensors[i] = tempSensors[i] ? tempSensors[i] - 273 : 0;
828 }
829 if (is_json) {
830 struct json_object *stats = json_create_object()json_object_new_object();
831 char tempstr[64] = { 0 };
832
833 root = json_create_object()json_object_new_object();
834 logPages = json_create_array()json_object_new_array();
835 json_object_add_value_array(root, "Micron temperature information", logPages)json_object_object_add(root, "Micron temperature information"
, logPages)
;
836 snprintf(tempstr, sizeof(tempstr), "%u C", temperature);
837 json_object_add_value_string(stats, "Current Composite Temperature", tempstr);
838 for (i = 0; i < SensorCount8; i++) {
839 char sensor_str[256] = { 0 };
840 char datastr[64] = { 0 };
841
842 if (!smart_log.temp_sensor[i])
843 continue;
844 snprintf(sensor_str, sizeof(sensor_str),
845 "Temperature Sensor #%d", (i + 1));
846 snprintf(datastr, sizeof(datastr), "%u C",
847 tempSensors[i]);
848 json_object_add_value_string(stats, sensor_str, datastr);
849 }
850 json_array_add_value_object(logPages, stats)json_object_array_add(logPages, stats);
851 json_print_object(root, NULL)printf("%s", json_object_to_json_string_ext(root, (1 <<
1) | (1 << 4)))
;
852 printf("\n");
853 json_free_object(root)json_object_put(root);
854 } else {
855 printf("Micron temperature information:\n");
856 printf("%-10s : %u C\n", "Current Composite Temperature", temperature);
857 for (i = 0; i < SensorCount8; i++) {
858 if (!smart_log.temp_sensor[i])
859 continue;
860 printf("%-10s%d : %u C\n", "Temperature Sensor #", i + 1, tempSensors[i]);
861 }
862 }
863 }
864 return err;
865}
866
867struct pcie_error_counters {
868 __u16 receiver_error;
869 __u16 bad_tlp;
870 __u16 bad_dllp;
871 __u16 replay_num_rollover;
872 __u16 replay_timer_timeout;
873 __u16 advisory_non_fatal_error;
874 __u16 DLPES;
875 __u16 poisoned_tlp;
876 __u16 FCPC;
877 __u16 completion_timeout;
878 __u16 completion_abort;
879 __u16 unexpected_completion;
880 __u16 receiver_overflow;
881 __u16 malformed_tlp;
882 __u16 ecrc_error;
883 __u16 unsupported_request_error;
884} pcie_error_counters = { 0 };
885
886struct {
887 const char *err;
888 int bit;
889 int val;
890} pcie_uncorrectable_errors[] = {
891 { (char *)"Unsupported Request Error Status (URES)", 20,
892 offsetof(struct pcie_error_counters, unsupported_request_error)__builtin_offsetof(struct pcie_error_counters, unsupported_request_error
)
},
893 { (char *)"ECRC Error Status (ECRCES)", 19,
894 offsetof(struct pcie_error_counters, ecrc_error)__builtin_offsetof(struct pcie_error_counters, ecrc_error)},
895 { (char *)"Malformed TLP Status (MTS)", 18,
896 offsetof(struct pcie_error_counters, malformed_tlp)__builtin_offsetof(struct pcie_error_counters, malformed_tlp)},
897 { (char *)"Receiver Overflow Status (ROS)", 17,
898 offsetof(struct pcie_error_counters, receiver_overflow)__builtin_offsetof(struct pcie_error_counters, receiver_overflow
)
},
899 { (char *)"Unexpected Completion Status (UCS)", 16,
900 offsetof(struct pcie_error_counters, unexpected_completion)__builtin_offsetof(struct pcie_error_counters, unexpected_completion
)
},
901 { (char *)"Completer Abort Status (CAS)", 15,
902 offsetof(struct pcie_error_counters, completion_abort)__builtin_offsetof(struct pcie_error_counters, completion_abort
)
},
903 { (char *)"Completion Timeout Status (CTS)", 14,
904 offsetof(struct pcie_error_counters, completion_timeout)__builtin_offsetof(struct pcie_error_counters, completion_timeout
)
},
905 { (char *)"Flow Control Protocol Error Status (FCPES)", 13,
906 offsetof(struct pcie_error_counters, FCPC)__builtin_offsetof(struct pcie_error_counters, FCPC)},
907 { (char *)"Poisoned TLP Status (PTS)", 12,
908 offsetof(struct pcie_error_counters, poisoned_tlp)__builtin_offsetof(struct pcie_error_counters, poisoned_tlp)},
909 { (char *)"Data Link Protocol Error Status (DLPES)", 4,
910 offsetof(struct pcie_error_counters, DLPES)__builtin_offsetof(struct pcie_error_counters, DLPES)},
911 },
912 pcie_correctable_errors[] = {
913 { (char *)"Advisory Non-Fatal Error Status (ANFES)", 13,
914 offsetof(struct pcie_error_counters, advisory_non_fatal_error)__builtin_offsetof(struct pcie_error_counters, advisory_non_fatal_error
)
},
915 { (char *)"Replay Timer Timeout Status (RTS)", 12,
916 offsetof(struct pcie_error_counters, replay_timer_timeout)__builtin_offsetof(struct pcie_error_counters, replay_timer_timeout
)
},
917 { (char *)"REPLAY_NUM Rollover Status (RRS)", 8,
918 offsetof(struct pcie_error_counters, replay_num_rollover)__builtin_offsetof(struct pcie_error_counters, replay_num_rollover
)
},
919 { (char *)"Bad DLLP Status (BDS)", 7,
920 offsetof(struct pcie_error_counters, bad_dllp)__builtin_offsetof(struct pcie_error_counters, bad_dllp)},
921 { (char *)"Bad TLP Status (BTS)", 6,
922 offsetof(struct pcie_error_counters, bad_tlp)__builtin_offsetof(struct pcie_error_counters, bad_tlp)},
923 { (char *)"Receiver Error Status (RES)", 0,
924 offsetof(struct pcie_error_counters, receiver_error)__builtin_offsetof(struct pcie_error_counters, receiver_error
)
},
925 };
926
927static int micron_pcie_stats(int argc, char **argv,
928 struct command *command, struct plugin *plugin)
929{
930 int i, err = 0;
931 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
932 __cleanup_nvme_transport_handle__attribute__((cleanup(cleanup_nvme_transport_handle))) struct libnvme_transport_handle *hdl = NULL((void*)0);
933 nvme_print_flags_t format = NORMAL;
934 struct libnvme_passthru_cmd admin_cmd = { 0 };
935 enum eDriveModel eModel = UNKNOWN_MODEL;
936 bool_Bool is_json = false0;
937 bool_Bool counters = false0;
938 const char *desc = "Retrieve PCIe event counters";
939
940 __u32 correctable_errors = 0;
941 __u32 uncorrectable_errors = 0;
942
943 OPT_ARGS(opts)struct argconfig_commandline_options opts[] = {
944 OPT_END(){ ((void*)0) }
945 };
946
947 err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &eModel);
948 if (err)
949 return err;
950
951 format = nvme_args.output_flags;
952 is_json = format == JSON;
953
954 /* pull log details based on the model name */
955 if (eModel == UNKNOWN_MODEL) {
956 nvme_show_error("Unsupported drive model for vs-pcie-stats command")nvme_show_message(1, "Unsupported drive model for vs-pcie-stats command"
)
;
957 err = -ENOTSUP95;
958 goto out;
959 }
960
961 if (eModel == M5407) {
962 admin_cmd.opcode = 0xD6;
963 admin_cmd.addr = (__u64)(uintptr_t)&pcie_error_counters;
964 admin_cmd.data_len = sizeof(pcie_error_counters);
965 admin_cmd.cdw10 = 1;
966 err = libnvme_exec_admin_passthru(hdl, &admin_cmd);
967 if (!err) {
968 counters = true1;
969 correctable_errors = 10;
970 uncorrectable_errors = 6;
971 goto print_stats;
972 }
973 }
974
975 err = micron_get_pcie_aer_errors(hdl, &correctable_errors,
976 &uncorrectable_errors);
977 if (err)
978 goto out;
979print_stats:
980 if (is_json) {
981 struct json_object *root = json_create_object()json_object_new_object();
982 struct json_object *pcieErrors = json_create_array()json_object_new_array();
983 struct json_object *stats = json_create_object()json_object_new_object();
984 __u8 *pcounter = (__u8 *)&pcie_error_counters;
985
986 json_object_add_value_array(root, "PCIE Stats", pcieErrors)json_object_object_add(root, "PCIE Stats", pcieErrors);
987 for (i = 0; i < ARRAY_SIZE(pcie_uncorrectable_errors)(sizeof(pcie_uncorrectable_errors) / sizeof((pcie_uncorrectable_errors
)[0]) + 0)
; i++) {
988 __u16 val = counters ? *(__u16 *)(pcounter + pcie_uncorrectable_errors[i].val) :
989 (uncorrectable_errors >>
990 pcie_uncorrectable_errors[i].bit) & 1;
991 json_object_add_value_int(stats, pcie_uncorrectable_errors[i].err, val)json_object_object_add(stats, pcie_uncorrectable_errors[i].err
, json_object_new_int(val))
;
992 }
993 for (i = 0; i < ARRAY_SIZE(pcie_correctable_errors)(sizeof(pcie_correctable_errors) / sizeof((pcie_correctable_errors
)[0]) + 0)
; i++) {
994 __u16 val = counters ? *(__u16 *)(pcounter + pcie_correctable_errors[i].val) :
995 (correctable_errors >> pcie_correctable_errors[i].bit) & 1;
996 json_object_add_value_int(stats, pcie_correctable_errors[i].err, val)json_object_object_add(stats, pcie_correctable_errors[i].err,
json_object_new_int(val))
;
997 }
998 json_array_add_value_object(pcieErrors, stats)json_object_array_add(pcieErrors, stats);
999 json_print_object(root, NULL)printf("%s", json_object_to_json_string_ext(root, (1 <<
1) | (1 << 4)))
;
1000 printf("\n");
1001 json_free_object(root)json_object_put(root);
1002 } else if (counters == true1) {
1003 __u8 *pcounter = (__u8 *)&pcie_error_counters;
1004
1005 for (i = 0; i < ARRAY_SIZE(pcie_uncorrectable_errors)(sizeof(pcie_uncorrectable_errors) / sizeof((pcie_uncorrectable_errors
)[0]) + 0)
; i++)
1006 printf("%-42s : %-1hu\n", pcie_uncorrectable_errors[i].err,
1007 *(__u16 *)(pcounter + pcie_uncorrectable_errors[i].val));
1008 for (i = 0; i < ARRAY_SIZE(pcie_correctable_errors)(sizeof(pcie_correctable_errors) / sizeof((pcie_correctable_errors
)[0]) + 0)
; i++)
1009 printf("%-42s : %-1hu\n", pcie_correctable_errors[i].err,
1010 *(__u16 *)(pcounter + pcie_correctable_errors[i].val));
1011 } else if (eModel == M5407 || eModel == M5410) {
1012 for (i = 0; i < ARRAY_SIZE(pcie_uncorrectable_errors)(sizeof(pcie_uncorrectable_errors) / sizeof((pcie_uncorrectable_errors
)[0]) + 0)
; i++)
1013 printf("%-42s : %-1d\n", pcie_uncorrectable_errors[i].err,
1014 ((uncorrectable_errors >>
1015 pcie_uncorrectable_errors[i].bit) & 1));
1016 for (i = 0; i < ARRAY_SIZE(pcie_correctable_errors)(sizeof(pcie_correctable_errors) / sizeof((pcie_correctable_errors
)[0]) + 0)
; i++)
1017 printf("%-42s : %-1d\n", pcie_correctable_errors[i].err,
1018 ((correctable_errors >>
1019 pcie_correctable_errors[i].bit) & 1));
1020 } else {
1021 printf("PCIE Stats:\n");
1022 printf("Device correctable errors detected: 0x%x\n",
1023 correctable_errors);
1024 printf("Device uncorrectable errors detected: 0x%x\n",
1025 uncorrectable_errors);
1026 }
1027
1028out:
1029 return err;
1030}
1031
1032static int micron_clear_pcie_correctable_errors(int argc, char **argv,
1033 struct command *command,
1034 struct plugin *plugin)
1035{
1036 int err = -EINVAL22;
1037 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
1038 __cleanup_nvme_transport_handle__attribute__((cleanup(cleanup_nvme_transport_handle))) struct libnvme_transport_handle *hdl = NULL((void*)0);
1039 enum eDriveModel model = UNKNOWN_MODEL;
1040 struct libnvme_passthru_cmd admin_cmd = { 0 };
1041 const char *desc = "Clear PCIe Device Correctable Errors";
1042 __u64 result = 0;
1043 __u8 fid = MICRON_FEATURE_CLEAR_PCI_CORRECTABLE_ERRORS0xC3;
1044
1045 OPT_ARGS(opts)struct argconfig_commandline_options opts[] = {
1046 OPT_END(){ ((void*)0) }
1047 };
1048
1049 err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &model);
1050 if (err)
1051 return err;
1052
1053 /* For M51CX models, PCIe errors are cleared using 0xC3 feature
1054 * and for M5407 models, PCIe errors are cleared using 0xD6 command
1055 * If these fail, proceed with sysfs interface to set/clear bits
1056 */
1057 if (model == M51CX || model == M51BY || model == M51CY) {
1058 err = nvme_set_features_simple(hdl, 0, fid, false0, 1U << 31,
1059 &result);
1060 if (!err)
1061 err = (int)result;
1062 if (!err) {
1063 nvme_show_verbose_result("Device correctable errors cleared!")nvme_show_verbose_message("Device correctable errors cleared!"
)
;
1064 return 0;
1065 }
1066 } else if (model == M5407) {
1067 admin_cmd.opcode = 0xD6;
1068 admin_cmd.addr = 0;
1069 admin_cmd.cdw10 = 0;
1070 err = libnvme_exec_admin_passthru(hdl, &admin_cmd);
1071 if (!err) {
1072 nvme_show_verbose_result("Device correctable errors cleared!")nvme_show_verbose_message("Device correctable errors cleared!"
)
;
1073 return 0;
1074 }
1075 }
1076
1077 /* clear status bits using system commands */
1078 err = micron_clear_pcie_aer_correctable_errors(hdl);
1079
1080 return err;
1081}
1082
1083static struct logpage {
1084 const char *field;
1085 char datastr[128];
1086} d0_log_page[] = {
1087 { "NAND Writes (Bytes Written)", { 0 }},
1088 { "Program Failure Count", { 0 }},
1089 { "Erase Failures", { 0 }},
1090 { "Bad Block Count", { 0 }},
1091 { "NAND XOR/RAID Recovery Trigger Events", { 0 }},
1092 { "NSZE Change Supported", { 0 }},
1093 { "Number of NSZE Modifications", { 0 }}
1094};
1095
1096static void init_d0_log_page(__u8 *buf, __u8 nsze)
1097{
1098 unsigned int logD0[D0_log_size512/sizeof(int)] = { 0 };
1099 __u64 count_lo, count_hi, count;
1100
1101 memcpy(logD0, buf, sizeof(logD0));
1102
1103
1104 count = ((__u64)logD0[45] << 32) | logD0[44];
1105 snprintf(d0_log_page[0].datastr, sizeof(d0_log_page[0].datastr),
1106 "0x%"PRIx64"l" "x", le64_to_cpu(count));
1107
1108 count_hi = ((__u64)logD0[39] << 32) | logD0[38];
1109 count_lo = ((__u64)logD0[37] << 32) | logD0[36];
1110 if (count_hi)
1111 snprintf(d0_log_page[1].datastr, sizeof(d0_log_page[1].datastr),
1112 "0x%"PRIx64"l" "x""%016"PRIx64"l" "x", le64_to_cpu(count_hi),
1113 le64_to_cpu(count_lo));
1114 else
1115 snprintf(d0_log_page[1].datastr, sizeof(d0_log_page[1].datastr),
1116 "0x%"PRIx64"l" "x", le64_to_cpu(count_lo));
1117
1118 count = ((__u64)logD0[25] << 32) | logD0[24];
1119 snprintf(d0_log_page[2].datastr, sizeof(d0_log_page[2].datastr),
1120 "0x%"PRIx64"l" "x", le64_to_cpu(count));
1121
1122 snprintf(d0_log_page[3].datastr, sizeof(d0_log_page[3].datastr), "0x%x",
1123 logD0[3]);
1124
1125 count_lo = ((__u64)logD0[37] << 32) | logD0[36];
1126 count = ((__u64)logD0[25] << 32) | logD0[24];
1127 count = (__u64)logD0[3] - (count_lo + count);
1128 snprintf(d0_log_page[4].datastr, sizeof(d0_log_page[4].datastr),
1129 "0x%"PRIx64"l" "x", le64_to_cpu(count));
1130
1131 snprintf(d0_log_page[5].datastr, sizeof(d0_log_page[5].datastr), "0x%x",
1132 nsze);
1133 snprintf(d0_log_page[6].datastr, sizeof(d0_log_page[6].datastr), "0x%x",
1134 logD0[1]);
1135}
1136
1137static struct nand_stats {
1138 const char *field;
1139 char datastr[128];
1140} hyperscale_BSSD_nand_stats[] = {
1141 {"Physical Media Units Written - TLC", {0}},
1142 {"Physical Media Units Written - SLC", {0}},
1143 {"Bad User NAND Block Count (Normalized)", {0}},
1144 {"Bad User NAND Block Count (Raw)", {0}},
1145 {"XOR Recovery count", {0}},
1146 {"Uncorrectable read error count", {0}},
1147 { "User Data Erase Counts (Minimum TLC)", {0}},
1148 { "User Data Erase Counts (Maximum TLC)", {0}},
1149 { "User Data Erase Counts (Average TLC)", {0}},
1150 { "User Data Erase Counts (Minimum SLC)", {0}},
1151 { "User Data Erase Counts (Maximum SLC)", {0}},
1152 { "User Data Erase Counts (Average SLC)", {0}},
1153 { "Program Fail Count (Normalized)", {0}},
1154 { "Program Fail Count (Raw)", {0}},
1155 { "Erase Fail Count (Normalized)", {0}},
1156 { "Erase Fail Count (Raw)", {0}},
1157 { "Total # of Soft ECC Error Count", {0}},
1158 { "Bad System NAND Block Count (Normalized)", {0}},
1159 { "Bad System NAND Block Count (Raw)", {0}},
1160 {"Endurance Estimate", {0}},
1161 {"Physical Media Units Read", {0}},
1162 {"Boot SSD Spec Version", {0}},
1163};
1164
1165/* @datastr is the datastr of an entry of hyperscale_BSSD_nand_stats */
1166static void micron_readFixedBytesFromBuffer(__u8 *buf, int offset, int numBytes, char *datastr)
1167{
1168 size_t size = sizeof(hyperscale_BSSD_nand_stats[0].datastr);
1169 uint64_t count_lo, count_hi, count;
1170
1171 switch (numBytes) {
1172 case 16:
1173 {
1174 count_lo = shr_get_unaligned_le64(&buf[offset]);
1175 count_hi = shr_get_unaligned_le64(&buf[offset + 8]);
1176 if (count_hi)
1177 snprintf(datastr, size, "0x%"PRIx64"l" "x""%016"PRIx64"l" "x""",
1178 count_hi, count_lo);
1179 else
1180 snprintf(datastr, size, "0x%"PRIx64"l" "x""", count_lo);
1181
1182 }
1183 break;
1184 case 8:
1185 {
1186 count = shr_get_unaligned_le64(&buf[offset]);
1187 snprintf(datastr, size, "0x%"PRIx64"l" "x""", count);
1188 }
1189 break;
1190 case 6:
1191 {
1192 count = shr_get_unaligned_le16(&buf[offset + 4]);
1193 count = (count << 32) | shr_get_unaligned_le32(&buf[offset]);
1194 snprintf(datastr, size, "0x%"PRIx64"l" "x""", count);
1195 }
1196 break;
1197 case 2:
1198 {
1199 snprintf(datastr, size, "0x%04x",
1200 shr_get_unaligned_le16(&buf[offset]));
1201 }
1202 break;
1203 }
1204}
1205
1206static void init_hyperscale_BSSD_nand_stats(__u8 *buf)
1207{
1208
1209 __u16 majorVer, minorVer, pointVer, errataVer;
1210
1211 micron_readFixedBytesFromBuffer(buf, 0, 16, hyperscale_BSSD_nand_stats[0].datastr);
1212 micron_readFixedBytesFromBuffer(buf, 16, 16, hyperscale_BSSD_nand_stats[1].datastr);
1213 micron_readFixedBytesFromBuffer(buf, 32, 2, hyperscale_BSSD_nand_stats[2].datastr);
1214 micron_readFixedBytesFromBuffer(buf, 34, 6, hyperscale_BSSD_nand_stats[3].datastr);
1215 micron_readFixedBytesFromBuffer(buf, 40, 8, hyperscale_BSSD_nand_stats[4].datastr);
1216 micron_readFixedBytesFromBuffer(buf, 48, 8, hyperscale_BSSD_nand_stats[5].datastr);
1217 micron_readFixedBytesFromBuffer(buf, 84, 8, hyperscale_BSSD_nand_stats[6].datastr);
1218 micron_readFixedBytesFromBuffer(buf, 92, 8, hyperscale_BSSD_nand_stats[7].datastr);
1219 micron_readFixedBytesFromBuffer(buf, 100, 8, hyperscale_BSSD_nand_stats[8].datastr);
1220 micron_readFixedBytesFromBuffer(buf, 108, 8, hyperscale_BSSD_nand_stats[9].datastr);
1221 micron_readFixedBytesFromBuffer(buf, 116, 8, hyperscale_BSSD_nand_stats[10].datastr);
1222 micron_readFixedBytesFromBuffer(buf, 124, 8, hyperscale_BSSD_nand_stats[11].datastr);
1223 micron_readFixedBytesFromBuffer(buf, 132, 2, hyperscale_BSSD_nand_stats[12].datastr);
1224 micron_readFixedBytesFromBuffer(buf, 134, 6, hyperscale_BSSD_nand_stats[13].datastr);
1225 micron_readFixedBytesFromBuffer(buf, 140, 2, hyperscale_BSSD_nand_stats[14].datastr);
1226 micron_readFixedBytesFromBuffer(buf, 142, 6, hyperscale_BSSD_nand_stats[15].datastr);
1227 micron_readFixedBytesFromBuffer(buf, 202, 8, hyperscale_BSSD_nand_stats[16].datastr);
1228 micron_readFixedBytesFromBuffer(buf, 218, 2, hyperscale_BSSD_nand_stats[17].datastr);
1229 micron_readFixedBytesFromBuffer(buf, 220, 6, hyperscale_BSSD_nand_stats[18].datastr);
1230 micron_readFixedBytesFromBuffer(buf, 226, 16, hyperscale_BSSD_nand_stats[19].datastr);
1231 micron_readFixedBytesFromBuffer(buf, 252, 16, hyperscale_BSSD_nand_stats[20].datastr);
1232
1233 majorVer = *((__u16 *)(&buf[300]));
1234 minorVer = *((__u16 *)(&buf[302]));
1235 pointVer = *((__u16 *)(&buf[304]));
1236 errataVer = *((__u16 *)(&buf[306]));
1237 snprintf(hyperscale_BSSD_nand_stats[21].datastr,
1238 sizeof(hyperscale_BSSD_nand_stats[21].datastr), "%x.%x.%x.%x",
1239 le16_to_cpu(majorVer), le16_to_cpu(minorVer),
1240 le16_to_cpu(pointVer), le16_to_cpu(errataVer));
1241}
1242
1243
1244/* Smart Health Log information as per OCP spec M51CX models */
1245struct request_data ocp_c0_log_page[] = {
1246 { "Physical Media Units Written", 16},
1247 { "Physical Media Units Read", 16 },
1248 { "Raw Bad User NAND Block Count", 6},
1249 { "Normalized Bad User NAND Block Count", 2},
1250 { "Raw Bad System NAND Block Count", 6},
1251 { "Normalized Bad System NAND Block Count", 2},
1252 { "XOR Recovery Count", 8},
1253 { "Uncorrectable Read Error Count", 8},
1254 { "Soft ECC Error Count", 8},
1255 { "SSD End to End Detected Counts", 4},
1256 { "SSD End to End Corrected Errors", 4},
1257 { "System data % life-used", 1},
1258 { "Refresh Count", 7},
1259 { "Maximum User Data Erase Count", 4},
1260 { "Minimum User Data Erase Count", 4},
1261 { "Thermal Throttling Count", 1},
1262 { "Thermal Throttling Status", 1},
1263 { "Reserved", 6},
1264 { "PCIe Correctable Error count", 8},
1265 { "Incomplete Shutdowns", 4},
1266 { "Reserved", 4},
1267 { "% Free Blocks", 1},
1268 { "Reserved", 7},
1269 { "Capacitor Health", 2},
1270 { "Reserved", 6},
1271 { "Unaligned I/O", 8},
1272 { "Security Version Number", 8},
1273 { "NUSE", 8},
1274 { "PLP Start Count", 16},
1275 { "Endurance Estimate", 16},
1276 { "Reserved", 302},
1277 { "Log Page Version", 2},
1278 { "Log Page GUID", 16},
1279},
1280
1281/* Smart Health Log information Extended as per Hyperscale NVME Boot SSD spec M51CX models */
1282hyperscale_c0_log_page[] = {
1283 { "Physical Media Units Written - TLC", 16},
1284 { "Physical Media Units Written - SLC", 16},
1285 { "Bad User NAND Block Count (Normalized)", 2},
1286 { "Bad User NAND Block Count (Raw)", 6},
1287 { "XOR Recovery Count", 8},
1288 { "Uncorrectable Read Error Count", 8},
1289 { "SSD End to End correction counts (Corrected Errors)", 8},
1290 { "SSD End to End correction counts (Detected Counts)", 8},
1291 { "SSD End to End correction counts (Uncorrected Counts)", 8},
1292 { "System data % life-used", 1},
1293 { "Reserved", 3},
1294 { "User Data Erase Counts (Minimum TLC)", 8},
1295 { "User Data Erase Counts (Maximum TLC)", 8},
1296 { "User Data Erase Counts (Average TLC)", 8},
1297 { "User Data Erase Counts (Minimum SLC)", 8},
1298 { "User Data Erase Counts (Maximum SLC)", 8},
1299 { "User Data Erase Counts (Average SLC)", 8},
1300 { "Program Fail Count (Normalized)", 2},
1301 { "Program Fail Count (Raw)", 6},
1302 { "Erase Fail Count (Normalized)", 2},
1303 { "Erase Fail Count (Raw)", 6},
1304 { "Pcie Correctable Error Count", 8},
1305 { "% Free Blocks (User)", 1},
1306 { "Reserved", 3},
1307 { "Security Version Number", 8},
1308 { "% Free Blocks (System)", 1},
1309 { "Reserved", 3},
1310 { "NVMe Stats (# Data set Management/TRIM Commands Completed", 16},
1311 { "Total Namespace Utilization (nvme0n1 NUSE)", 8},
1312 { "NVMe Stats (#NVMe Format Commands Completed)", 2},
1313 { "Background Back-Pressure Gauge(%)", 1},
1314 { "Reserved", 3},
1315 { "Total # of Soft ECC Error Count", 8},
1316 { "Total # of Read Refresh Count", 8},
1317 { "Bad System NAND Block Count (Normalized)", 2},
1318 { "Bad System NAND Block Count (Raw)", 6},
1319 { "Endurance Estimate (Total Writable Lifetime Bytes)", 16},
1320 { "Thermal Throttling Status & Count (Number of thermal throttling events)", 2},
1321 { "Total # Unaligned I/O", 8},
1322 { "Total Physical Media Units Read (Bytes)", 16},
1323 { "Command Timeout (# of READ CMDs exceeding threshold)", 4},
1324 { "Command Timeout (# of WRITE CMDs exceeding threshold)", 4},
1325 { "Command Timeout (# of TRIMs CMDs exceeding threshold)", 4},
1326 { "Reserved", 4},
1327 { "Total PCIe Link Retraining Count", 8},
1328 { "Active Power State Change Count", 8},
1329 { "Boot SSD Spec Version", 8},
1330 { "FTL Unit Size", 4},
1331 { "TCG Ownership Status", 4},
1332 { "Reserved", 178},
1333 { "Log Page Version", 2},
1334 { "Log Page GUID", 16},
1335},
1336
1337/* Smart Health Log information as per datacenter-nvme-ssd-specification-v2 M51BY models */
1338datacenter_c0_log_page[] = {
1339 {"Physical Media Units Written", 16},
1340 {"Physical Media Units Read", 16 },
1341 {"Raw Bad User NAND Block Count", 6},
1342 {"Normalized Bad User NAND Block Count", 2},
1343 {"Raw Bad System NAND Block Count", 6},
1344 {"Normalized Bad System NAND Block Count", 2},
1345 {"XOR Recovery Count", 8},
1346 {"Uncorrectable Read Error Count", 8},
1347 {"Soft ECC Error Count", 8},
1348 {"SSD End to End Detected Counts", 4},
1349 {"SSD End to End Corrected Errors", 4},
1350 {"System data % life-used", 1},
1351 {"Refresh Count", 7},
1352 {"Maximum User Data Erase Count", 4},
1353 {"Minimum User Data Erase Count", 4},
1354 {"Thermal Throttling Count", 1},
1355 {"Thermal Throttling Status", 1},
1356 {"DSSD Spec Version", 6},
1357 {"PCIe Correctable Error count", 8},
1358 {"Incomplete Shutdowns", 4},
1359 {"Reserved", 4},
1360 {"% Free Blocks", 1},
1361 {"Reserved", 7},
1362 {"Capacitor Health", 2},
1363 {"NVMe Errata Version", 1},
1364 {"Reserved", 5},
1365 {"Unaligned I/O", 8},
1366 {"Security Version Number", 8},
1367 {"Total NUSE", 8},
1368 {"PLP Start Count", 16},
1369 {"Endurance Estimate", 16},
1370 {"PCIe Link Retraining Count", 8},
1371 {"Power State Change Count", 8},
1372 {"Reserved", 286},
1373 {"Log Page Version", 2},
1374 {"Log Page GUID", 16},
1375},
1376
1377/* Extended SMART log information */
1378e1_log_page[] = {
1379 {"Reserved", 12},
1380 {"Grown Bad Block Count", 4},
1381 {"Per Block Max Erase Count", 4},
1382 {"Power On Minutes", 4},
1383 {"Reserved", 24},
1384 {"Write Protect Reason", 4},
1385 {"Reserved", 12},
1386 {"Drive Capacity", 8},
1387 {"Reserved", 8},
1388 {"Total Erase Count", 8},
1389 {"Lifetime Use Rate", 8},
1390 {"Erase Fail Count", 8},
1391 {"Reserved", 8},
1392 {"Reported UC Errors", 8},
1393 {"Reserved", 24},
1394 {"Program Fail Count", 16},
1395 {"Total Bytes Read", 16},
1396 {"Total Bytes Written", 16},
1397 {"Reserved", 16},
1398 {"TU Size", 4},
1399 {"Total Block Stripe Count", 4},
1400 {"Free Block Stripe Count", 4},
1401 {"Block Stripe Size", 8},
1402 {"Reserved", 16},
1403 {"User Block Min Erase Count", 4},
1404 {"User Block Avg Erase Count", 4},
1405 {"User Block Max Erase Count", 4},
1406},
1407/* Vendor Specific Health Log information */
1408fb_log_page[] = {
1409 {"Physical Media Units Written - TLC", 16, 16 },
1410 {"Physical Media Units Written - SLC", 16, 16 },
1411 {"Normalized Bad User NAND Block Count", 2, 2},
1412 {"Raw Bad User NAND Block Count", 6, 6},
1413 {"XOR Recovery Count", 8, 8},
1414 {"Uncorrectable Read Error Count", 8, 8},
1415 {"SSD End to End Corrected Errors", 8, 8},
1416 {"SSD End to End Detected Counts", 4, 8},
1417 {"SSD End to End Uncorrected Counts", 4, 8},
1418 {"System data % life-used", 1, 1},
1419 {"Reserved", 0, 3},
1420 {"Minimum User Data Erase Count - TLC", 8, 8},
1421 {"Maximum User Data Erase Count - TLC", 8, 8},
1422 {"Average User Data Erase Count - TLC", 0, 8},
1423 {"Minimum User Data Erase Count - SLC", 8, 8},
1424 {"Maximum User Data Erase Count - SLC", 8, 8},
1425 {"Average User Data Erase Count - SLC", 0, 8},
1426 {"Normalized Program Fail Count", 2, 2},
1427 {"Raw Program Fail Count", 6, 6},
1428 {"Normalized Erase Fail Count", 2, 2},
1429 {"Raw Erase Fail Count", 6, 6},
1430 {"Pcie Correctable Error Count", 8, 8},
1431 {"% Free Blocks (User)", 1, 1},
1432 {"Reserved", 0, 3},
1433 {"Security Version Number", 8, 8},
1434 {"% Free Blocks (System)", 1, 1},
1435 {"Reserved", 0, 3},
1436 {"Dataset Management (Deallocate) Commands", 16, 16},
1437 {"Incomplete TRIM Data", 8, 8},
1438 {"% Age of Completed TRIM", 1, 2},
1439 {"Background Back-Pressure Gauge", 1, 1},
1440 {"Reserved", 0, 3},
1441 {"Soft ECC Error Count", 8, 8},
1442 {"Refresh Count", 8, 8},
1443 {"Normalized Bad System NAND Block Count", 2, 2},
1444 {"Raw Bad System NAND Block Count", 6, 6},
1445 {"Endurance Estimate", 16, 16},
1446 {"Thermal Throttling Status", 1, 1},
1447 {"Thermal Throttling Count", 1, 1},
1448 {"Unaligned I/O", 8, 8},
1449 {"Physical Media Units Read", 16, 16},
1450 {"Reserved", 279, 0},
1451 {"Log Page Version", 2, 0},
1452 {"READ CMDs exceeding threshold", 0, 4},
1453 {"WRITE CMDs exceeding threshold", 0, 4},
1454 {"TRIMs CMDs exceeding threshold", 0, 4},
1455 {"Reserved", 0, 4},
1456 {"Reserved", 0, 210},
1457 {"Log Page Version", 0, 2},
1458 {"Log Page GUID", 0, 16},
1459},
1460
1461/* SMARTS for 0x6001 Nitro model */
1462//Extended Health Information (Log Identifier D0h)
1463D0_log_page[] = {
1464 {"Reserved", 2},//1:0
1465 {"Version", 2},//3:2
1466 {"Grown Bad Block Count", 4},//7:4
1467 {"Total SRAM SBE Count", 4},//11:8
1468 {"Total SRAM DBE Count", 4},//15:12
1469 {"Write Protect Reason", 4},//19:16
1470 {"Total Erase Count", 4},//23:20
1471 {"Erase Fail Count", 4},//27:24
1472 {"Program Fail Counts", 4},//31:28
1473 {"Reserved", 40},//71:32
1474 {"Completed PLN PLA Cycles", 4},//75:72
1475 {"PLN Assert Count", 4},//79:76
1476 {"PLN Deassert Count", 4},//83:80
1477 {"PLA Assert Count", 4},//87:84
1478 {"PLA Deassert Count", 4},//91:88
1479 {"Correctable NAND UECCs", 4},//95:92
1480 {"Reported Uncorrectable Errors UECCCs", 4},//99:96
1481 {"TLC Super Block Min Erase Count", 4},//103:100
1482 {"TLC Super Block Avg Erase Count", 4},//107:104
1483 {"TLC Super Block Max Erase Count", 4},//111:108
1484 {"Min ASIC Temp Recorded", 2},//113:112
1485 {"Max ASIC Temp Recorded", 2},//115:114
1486 {"Min NAND Temp Recorded", 2},//171:116
1487 {"Max NAND Temp Recorded", 2},//119:118
1488 {"SLC Super Block Min Erase Count", 4},//123:120
1489 {"SLC Super Block Avg Erase Count", 4},//127:124
1490 {"SLC Super Block Max Erase Count", 4},//131:128
1491 {"SLC Lifetime Used", 2},//133:132
1492 {"TLC Lifetime Used", 2},//135:134
1493 {"Unmapped LBA Count", 8},//143:136
1494 {"PWR1 Voltage Detection Threshold #1", 4},//147:144
1495 {"PWR1 Voltage Detection Threshold #2", 4},//151:148
1496},
1497//Micron Workload Log (Log Identifier C5h)
1498C5_log_page[] = {
1499 {"Reserved", 4},//3:0
1500 {"Number of Downshifts", 1},//4
1501 {"Reserved", 71},//75:5
1502 {"Number of LBAs Deallocated Trimmed", 4},//79:76
1503 {"Reserved", 41},//120:80
1504 {"Number of Security Send Commands", 4},//124:121
1505 {"Number of Security Receive Commands", 4},//128:125
1506 {"Total Sanitize Events", 4},//132:129
1507},
1508//Vendor Telemetry Log (Log Identifier C6h))
1509C6_log_page[] = {
1510 {"Reserved", 240},//239:0
1511 {"Total TLC NAND Write Count", 8},//247:240
1512 {"Total SLC NAND Write Count", 8},//255:248
1513 {"Total SLC Host Write Count", 8},//263:256
1514 {"Total TLC Host Write Count", 8},//272:264
1515};
1516
1517static void print_smart_cloud_health_log(__u8 *buf, bool_Bool is_json, enum eDriveModel eModel)
1518{
1519 __cleanup_json_object__attribute__((cleanup(json_free_objectp))) struct json_object *stats = NULL((void*)0);
1520 struct json_object *root = NULL((void*)0);
1521 struct json_object *logPages = NULL((void*)0);
1522 const char *name = NULL((void*)0);
1523 int field_count = 0;
1524
1525 if (eModel == M51CX) {
1526 field_count = ARRAY_SIZE(ocp_c0_log_page)(sizeof(ocp_c0_log_page) / sizeof((ocp_c0_log_page)[0]) + 0);
1527 name = "OCP SMART Cloud Health Log: 0xC0";
1528 } else if (eModel == M51BY || eModel == M51CY) {
1529 field_count = ARRAY_SIZE(datacenter_c0_log_page)(sizeof(datacenter_c0_log_page) / sizeof((datacenter_c0_log_page
)[0]) + 0)
;
1530 name = "OCP DataCenter SMART Health Log: 0xC0";
1531 }
1532
1533 if (is_json) {
1534 root = json_create_object()json_object_new_object();
1535 stats = json_create_object()json_object_new_object();
1536 /* no layout of the other models, e.g. M6003 and M6004 */
1537 if (name) {
1538 logPages = json_create_array()json_object_new_array();
1539 json_object_add_value_array(root, name, logPages)json_object_object_add(root, name, logPages);
1540 }
1541 }
1542
1543 if (eModel == M51BY || eModel == M51CY)
1544 generic_structure_parser(buf, datacenter_c0_log_page, field_count, stats, 0, NULL((void*)0));
1545 else if (eModel == M51CX)
1546 generic_structure_parser(buf, ocp_c0_log_page, field_count, stats, 0, NULL((void*)0));
1547
1548 if (is_json) {
1549 if (logPages) {
1550 json_array_add_value_object(logPages, stats)json_object_array_add(logPages, stats);
1551 stats = NULL((void*)0);
1552 }
1553 json_print_object(root, NULL)printf("%s", json_object_to_json_string_ext(root, (1 <<
1) | (1 << 4)))
;
1554 printf("\n");
1555 json_free_object(root)json_object_put(root);
1556 }
1557}
1558
1559static void print_hyperscale_cloud_health_log(__u8 *buf, bool_Bool is_json)
1560{
1561 struct json_object *root;
1562 struct json_object *logPages;
1563 struct json_object *stats = NULL((void*)0);
1564 int field_count = ARRAY_SIZE(hyperscale_c0_log_page)(sizeof(hyperscale_c0_log_page) / sizeof((hyperscale_c0_log_page
)[0]) + 0)
;
1565
1566 if (is_json) {
1567 root = json_create_object()json_object_new_object();
1568 stats = json_create_object()json_object_new_object();
1569 logPages = json_create_array()json_object_new_array();
1570 json_object_add_value_array(root, "OCP Hyperscale Cloud Health Log: 0xC0",json_object_object_add(root, "OCP Hyperscale Cloud Health Log: 0xC0"
, logPages)
1571 logPages)json_object_object_add(root, "OCP Hyperscale Cloud Health Log: 0xC0"
, logPages)
;
1572 }
1573
1574 generic_structure_parser(buf, hyperscale_c0_log_page, field_count, stats, 0, NULL((void*)0));
1575
1576 if (is_json) {
1577 json_array_add_value_object(logPages, stats)json_object_array_add(logPages, stats);
1578 json_print_object(root, NULL)printf("%s", json_object_to_json_string_ext(root, (1 <<
1) | (1 << 4)))
;
1579 printf("\n");
1580 json_free_object(root)json_object_put(root);
1581 }
1582}
1583
1584static void print_nand_stats_fb(__u8 *buf, __u8 *buf2, __u8 nsze, bool_Bool is_json, __u8 spec)
1585{
1586 struct json_object *root;
1587 struct json_object *logPages;
1588 struct json_object *stats = NULL((void*)0);
1589 int field_count = ARRAY_SIZE(fb_log_page)(sizeof(fb_log_page) / sizeof((fb_log_page)[0]) + 0);
1590
1591 if (is_json) {
1592 root = json_create_object()json_object_new_object();
1593 stats = json_create_object()json_object_new_object();
1594 logPages = json_create_array()json_object_new_array();
1595 json_object_add_value_array(root, "Extended Smart Log Page : 0xFB",json_object_object_add(root, "Extended Smart Log Page : 0xFB"
, logPages)
1596 logPages)json_object_object_add(root, "Extended Smart Log Page : 0xFB"
, logPages)
;
1597 }
1598
1599 generic_structure_parser(buf, fb_log_page, field_count, stats, spec, NULL((void*)0));
1600
1601 /* print last three entries from D0 log page */
1602 if (buf2) {
1603 init_d0_log_page(buf2, nsze);
1604
1605 if (is_json) {
1606 for (int i = 0; i < 7; i++)
1607 json_object_add_value_string(stats,
1608 d0_log_page[i].field,
1609 d0_log_page[i].datastr);
1610 } else {
1611 for (int i = 0; i < 7; i++)
1612 printf("%-40s : %s\n", d0_log_page[i].field, d0_log_page[i].datastr);
1613 }
1614 }
1615
1616 if (is_json) {
1617 json_array_add_value_object(logPages, stats)json_object_array_add(logPages, stats);
1618 json_print_object(root, NULL)printf("%s", json_object_to_json_string_ext(root, (1 <<
1) | (1 << 4)))
;
1619 printf("\n");
1620 json_free_object(root)json_object_put(root);
1621 }
1622}
1623
1624static void print_nand_stats_d0(__u8 *buf, __u8 oacs, bool_Bool is_json)
1625{
1626 init_d0_log_page(buf, oacs);
1627
1628 if (is_json) {
1629 struct json_object *root = json_create_object()json_object_new_object();
1630 struct json_object *stats = json_create_object()json_object_new_object();
1631 struct json_object *logPages = json_create_array()json_object_new_array();
1632
1633 json_object_add_value_array(root, "Extended Smart Log Page : 0xD0", logPages)json_object_object_add(root, "Extended Smart Log Page : 0xD0"
, logPages)
;
1634
1635 for (int i = 0; i < 7; i++)
1636 json_object_add_value_string(stats, d0_log_page[i].field,
1637 d0_log_page[i].datastr);
1638
1639 json_array_add_value_object(logPages, stats)json_object_array_add(logPages, stats);
1640 json_print_object(root, NULL)printf("%s", json_object_to_json_string_ext(root, (1 <<
1) | (1 << 4)))
;
1641 printf("\n");
1642 json_free_object(root)json_object_put(root);
1643 } else {
1644 for (int i = 0; i < 7; i++)
1645 printf("%-40s : %s\n", d0_log_page[i].field, d0_log_page[i].datastr);
1646 }
1647}
1648
1649static void print_hyperscale_nand_stats(__u8 *buf, bool_Bool is_json)
1650{
1651 init_hyperscale_BSSD_nand_stats(buf);
1652
1653 if (is_json) {
1654 struct json_object *root = json_create_object()json_object_new_object();
1655 struct json_object *stats = json_create_object()json_object_new_object();
1656 struct json_object *logPages = json_create_array()json_object_new_array();
1657
1658 json_object_add_value_array(root, "Extended Smart Log Page : 0xC0", logPages)json_object_object_add(root, "Extended Smart Log Page : 0xC0"
, logPages)
;
1659
1660 for (int i = 0; i < 22; i++)
1661 json_object_add_value_string(stats,
1662 hyperscale_BSSD_nand_stats[i].field,
1663 hyperscale_BSSD_nand_stats[i].datastr);
1664
1665 json_array_add_value_object(logPages, stats)json_object_array_add(logPages, stats);
1666 json_print_object(root, NULL)printf("%s", json_object_to_json_string_ext(root, (1 <<
1) | (1 << 4)))
;
1667 printf("\n");
1668 json_free_object(root)json_object_put(root);
1669 } else {
1670 for (int i = 0; i < 22; i++)
1671 printf("%-40s : %s\n", hyperscale_BSSD_nand_stats[i].field,
1672 hyperscale_BSSD_nand_stats[i].datastr);
1673 }
1674}
1675
1676static bool_Bool nsze_from_oacs;/* read nsze for now from idd[4059] */
1677
1678static int micron_nand_stats(int argc, char **argv,
1679 struct command *command, struct plugin *plugin)
1680{
1681 const char *desc = "Retrieve Micron NAND stats for the given device ";
1682 unsigned int extSmartLog[D0_log_size512/sizeof(int)] = { 0 };
1683 unsigned int logFB[FB_log_size512/sizeof(int)] = { 0 };
1684 unsigned char logC0[C0_log_size512] = { 0 };
1685 enum eDriveModel eModel = UNKNOWN_MODEL;
1686 struct nvme_id_ctrl ctrl;
1687 struct libnvme_passthru_cmd cmd;
1688 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
1689 __cleanup_nvme_transport_handle__attribute__((cleanup(cleanup_nvme_transport_handle))) struct libnvme_transport_handle *hdl = NULL((void*)0);
1690 int err;
1691 __u8 nsze;
1692 bool_Bool has_d0_log = true1;
1693 bool_Bool has_fb_log = false0;
1694 bool_Bool is_json = false0;
1695 nsze_from_oacs = false0;
1696 nvme_print_flags_t format;
1697
1698 OPT_ARGS(opts)struct argconfig_commandline_options opts[] = {
1699 OPT_END(){ ((void*)0) }
1700 };
1701
1702 err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &eModel);
1703 if (err)
1704 return err;
1705
1706 format = nvme_args.output_flags;
1707 is_json = format == JSON;
1708
1709 /* pull log details based on the model name */
1710 if (eModel == UNKNOWN_MODEL) {
1711 nvme_show_error("Unsupported drive model for vs-nand-stats command")nvme_show_message(1, "Unsupported drive model for vs-nand-stats command"
)
;
1712 return -1;
1713 }
1714
1715 nvme_init_identify_ctrl(&cmd, &ctrl);
1716 err = libnvme_exec_admin_passthru(hdl, &cmd);
1717 if (err) {
1718 nvme_show_err(err, "ERROR : identify_ctrl() failed");
1719 return -1;
1720 }
1721
1722 if ((ctrl.vs[536] == MICRON_CUST_ID_GG0x16) && (eModel == M51CX)) {
1723 err = nvme_get_log_simple(hdl, 0xC0, logC0, C0_log_size512);
1724 if (err == 0) {
1725 print_hyperscale_nand_stats((__u8 *)logC0, is_json);
1726 goto out;
1727 } else if (err < 0) {
1728 nvme_show_err(err, "Unable to retrieve extended smart log 0xC0 for the drive");
1729 return -1;
1730 }
1731 }
1732
1733 err = nvme_get_log_simple(hdl, 0xD0, extSmartLog, D0_log_size512);
1734 has_d0_log = !err;
1735
1736 /* should check for firmware version if this log is supported or not */
1737 if (eModel != M5407 && eModel != M5410) {
1738 err = nvme_get_log_simple(hdl, 0xFB, logFB, FB_log_size512);
1739 has_fb_log = !err;
1740 }
1741
1742 nsze = (ctrl.vs[987] == 0x12);
1743 if (!nsze && nsze_from_oacs)
1744 nsze = ((ctrl.oacs >> 3) & 0x1);
1745
1746 if (has_fb_log) {
1747 __u8 spec = 1; /* FB spec version */
1748
1749 print_nand_stats_fb((__u8 *)logFB, (__u8 *)extSmartLog, nsze, is_json, spec);
1750 err = 0;
1751 } else if (has_d0_log) {
1752 print_nand_stats_d0((__u8 *)extSmartLog, nsze, is_json);
1753 err = 0;
1754 }
1755out:
1756 if (err)
1757 nvme_show_err(err, "Unable to retrieve extended smart log for the drive");
1758
1759 return err;
1760}
1761
1762static void print_log(__u8 *buf, bool_Bool is_json, unsigned char ucLogID)
1763{
1764 struct json_object *root;
1765 struct json_object *logPages;
1766 struct json_object *stats = NULL((void*)0);
1767 int field_count = 0;
1768 struct request_data *log_pageID;
1769 char tempStr[256] = { 0 };
1770
1771 if (ucLogID == 0xE1) {
1772 log_pageID = e1_log_page;
1773 field_count = ARRAY_SIZE(e1_log_page)(sizeof(e1_log_page) / sizeof((e1_log_page)[0]) + 0);
1774 snprintf(tempStr, sizeof(tempStr), "SMART Extended Log:0x%X",
1775 ucLogID);
1776 } else if (ucLogID == 0xD0) {
1777 log_pageID = D0_log_page;
1778 field_count = ARRAY_SIZE(D0_log_page)(sizeof(D0_log_page) / sizeof((D0_log_page)[0]) + 0);
1779 snprintf(tempStr, sizeof(tempStr), "SMART Extended Log:0x%X",
1780 ucLogID);
1781 } else if (ucLogID == 0xC5) {
1782 log_pageID = C5_log_page;
1783 field_count = ARRAY_SIZE(C5_log_page)(sizeof(C5_log_page) / sizeof((C5_log_page)[0]) + 0);
1784 snprintf(tempStr, sizeof(tempStr), "Micron Workload Log:0x%X",
1785 ucLogID);
1786 } else if (ucLogID == 0xC6) {
1787 log_pageID = C6_log_page;
1788 field_count = ARRAY_SIZE(C6_log_page)(sizeof(C6_log_page) / sizeof((C6_log_page)[0]) + 0);
1789 snprintf(tempStr, sizeof(tempStr), "Vendor Telemetry Log:0x%X",
1790 ucLogID);
1791 } else {
1792 log_pageID = NULL((void*)0);
1793 }
1794
1795 if (is_json) {
1796 root = json_create_object()json_object_new_object();
1797 stats = json_create_object()json_object_new_object();
1798 logPages = json_create_array()json_object_new_array();
1799 json_object_add_value_array(root, tempStr, logPages)json_object_object_add(root, tempStr, logPages);
1800 } else {
1801 printf("%s\n", tempStr);
1802 }
1803
1804 if (log_pageID != NULL((void*)0))
1805 generic_structure_parser(buf, log_pageID, field_count, stats, 0, NULL((void*)0));
1806
1807 if (is_json) {
1808 json_array_add_value_object(logPages, stats)json_object_array_add(logPages, stats);
1809 json_print_object(root, NULL)printf("%s", json_object_to_json_string_ext(root, (1 <<
1) | (1 << 4)))
;
1810 printf("\n");
1811 json_free_object(root)json_object_put(root);
1812 }
1813}
1814
1815static int micron_smart_ext_log(int argc, char **argv,
1816 struct command *command, struct plugin *plugin)
1817{
1818 const char *desc = "Retrieve extended SMART logs for the given device ";
1819 unsigned int extSmartLog[E1_log_size256/sizeof(int)] = { 0 };
1820 enum eDriveModel eModel = UNKNOWN_MODEL;
1821 int err = 0;
1822 __u8 log_id;
1823 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
1824 __cleanup_nvme_transport_handle__attribute__((cleanup(cleanup_nvme_transport_handle))) struct libnvme_transport_handle *hdl = NULL((void*)0);
1825 bool_Bool is_json = false0;
1826 nvme_print_flags_t format;
1827
1828 OPT_ARGS(opts)struct argconfig_commandline_options opts[] = {
1829 OPT_END(){ ((void*)0) }
1830 };
1831
1832 err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &eModel);
1833 if (err)
1834 return err;
1835
1836 format = nvme_args.output_flags;
1837 is_json = format == JSON;
1838
1839 if (eModel == M51CX || eModel == M51BY || eModel == M51CY || eModel == M6003 ||
1840 eModel == M6004) {
1841 log_id = 0xE1;
1842 } else if (eModel == M6001) {
1843 log_id = 0xD0;
1844 } else {
1845 nvme_show_error("Unsupported drive model for vs-smart-ext-log command")nvme_show_message(1, "Unsupported drive model for vs-smart-ext-log command"
)
;
1846 err = -1;
1847 goto out;
1848 }
1849 err = nvme_get_log_simple(hdl, log_id, extSmartLog, E1_log_size256);
1850 if (!err)
1851 print_log((__u8 *)extSmartLog, is_json, log_id);
1852
1853out:
1854 if (err > 0)
1855 nvme_show_status(err);
1856 return err;
1857}
1858
1859static int micron_work_load_log(int argc, char **argv, struct command *acmd, struct plugin *plugin)
1860{
1861 const char *desc = "Retrieve Micron Workload logs for the given device ";
1862 unsigned int micronWorkLoadLog[C5_MicronWorkLoad_log_size256/sizeof(int)] = { 0 };
1863 enum eDriveModel eModel = UNKNOWN_MODEL;
1864 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
1865 __cleanup_nvme_transport_handle__attribute__((cleanup(cleanup_nvme_transport_handle))) struct libnvme_transport_handle *hdl = NULL((void*)0);
1866
1867 int err = 0;
1868 bool_Bool is_json = false0;
1869 nvme_print_flags_t format;
1870
1871 OPT_ARGS(opts)struct argconfig_commandline_options opts[] = {
1872 OPT_END(){ ((void*)0) }
1873 };
1874
1875 err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &eModel);
1876 if (err)
1877 return err;
1878
1879 format = nvme_args.output_flags;
1880 is_json = format == JSON;
1881
1882 if (eModel == M6001 || eModel == M6004 || eModel == M6003) {
1883 err = nvme_get_log_simple(hdl, 0xC5,
1884 micronWorkLoadLog, C5_MicronWorkLoad_log_size256);
1885 if (!err)
1886 print_log((__u8 *)micronWorkLoadLog, is_json, 0xC5);
1887 } else {
1888 nvme_show_error("Unsupported drive model for vs-work-load-log command")nvme_show_message(1, "Unsupported drive model for vs-work-load-log command"
)
;
1889 err = -1;
1890 goto out;
1891 }
1892
1893out:
1894 if (err > 0)
1895 nvme_show_status(err);
1896 return err;
1897}
1898
1899static int micron_vendor_telemetry_log(int argc, char **argv,
1900 struct command *command, struct plugin *plugin)
1901{
1902 const char *desc = "Retrieve Vendor Telemetry logs for the given device ";
1903 unsigned int vendorTelemetryLog[C6_log_size512/sizeof(int)] = { 0 };
1904 enum eDriveModel eModel = UNKNOWN_MODEL;
1905 int err = 0;
1906 bool_Bool is_json = false0;
1907 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
1908 __cleanup_nvme_transport_handle__attribute__((cleanup(cleanup_nvme_transport_handle))) struct libnvme_transport_handle *hdl = NULL((void*)0);
1909
1910 nvme_print_flags_t format;
1911
1912 OPT_ARGS(opts)struct argconfig_commandline_options opts[] = {
1913 OPT_END(){ ((void*)0) }
1914 };
1915
1916 err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &eModel);
1917 if (err)
1918 return err;
1919
1920 format = nvme_args.output_flags;
1921 is_json = format == JSON;
1922
1923 if (eModel == M6001 || eModel == M6004 || eModel == M6003) {
1924 err = nvme_get_log_simple(hdl, 0xC6, vendorTelemetryLog, C6_log_size512);
1925 if (!err)
1926 print_log((__u8 *)vendorTelemetryLog, is_json, 0xC6);
1927 } else {
1928 nvme_show_error("Unsupported drive model for vs-vendor-telemetry-log command")nvme_show_message(1, "Unsupported drive model for vs-vendor-telemetry-log command"
)
;
1929 err = -1;
1930 goto out;
1931 }
1932
1933out:
1934 if (err > 0)
1935 nvme_show_status(err);
1936 return err;
1937}
1938
1939#define LOGPULL_METADATA_FILE"logpull_metadata_info.json" "logpull_metadata_info.json"
1940#define LOGPULL_CMD_STATUS_FILE"logpull_cmd_status_info.csv" "logpull_cmd_status_info.csv"
1941
1942#define LOGPULL_PACKAGE_VERSION"1.4" "1.4"
1943
1944#define LOGPULL_STATUS_SUCCESS"Success" "Success"
1945
1946/*
1947 * Binary file names for each log, command and feature vs-internal-log collects.
1948 * These are the names expected by log decoders, so they must match exactly.
1949 */
1950#define LOG_FILE_ID_CTRL"nvme_controller_identify_data.bin" "nvme_controller_identify_data.bin"
1951#define LOG_FILE_ID_NS"identify_namespace_%d_data.bin" "identify_namespace_%d_data.bin"
1952#define LOG_FILE_SMART"smart_data.bin" "smart_data.bin"
1953#define LOG_FILE_ERROR_INFO"error_information_log.bin" "error_information_log.bin"
1954#define LOG_FILE_FW_SLOT"firmware_slot_info_log.bin" "firmware_slot_info_log.bin"
1955#define LOG_FILE_CHANGED_NS"changed_namespace_log.bin" "changed_namespace_log.bin"
1956#define LOG_FILE_CMD_EFFECTS"command_effects_log.bin" "command_effects_log.bin"
1957#define LOG_FILE_SELF_TEST"drive_self_test.bin" "drive_self_test.bin"
1958#define LOG_FILE_TELEMETRY_HOST"nvme_host_telemetry_log.bin" "nvme_host_telemetry_log.bin"
1959#define LOG_FILE_TELEMETRY_CTRL"nvme_controller_telemetry_log.bin" "nvme_controller_telemetry_log.bin"
1960#define LOG_FILE_PEVENT"persistent_event_log.bin" "persistent_event_log.bin"
1961
1962/* Vendor log files are named after their log page identifier. */
1963#define LOG_FILE_VS_LOG(id)"nvmelog_" "id" ".bin" "nvmelog_" #id ".bin"
1964
1965#define LOG_FILE_FEAT_ARBITRATION"nvme_feature_setting_arbitration.bin" "nvme_feature_setting_arbitration.bin"
1966#define LOG_FILE_FEAT_POWER_MGMT"nvme_feature_setting_pm.bin" "nvme_feature_setting_pm.bin"
1967#define LOG_FILE_FEAT_LBA_RANGE"nvme_feature_setting_lba_range_namespace_1.bin" "nvme_feature_setting_lba_range_namespace_1.bin"
1968#define LOG_FILE_FEAT_TEMP_THRESHOLD"nvme_feature_setting_temp_threshold.bin" "nvme_feature_setting_temp_threshold.bin"
1969#define LOG_FILE_FEAT_ERROR_RECOVERY"nvme_feature_setting_error_recovery.bin" "nvme_feature_setting_error_recovery.bin"
1970#define LOG_FILE_FEAT_VWC"nvme_feature_setting_volatile_write_cache.bin" "nvme_feature_setting_volatile_write_cache.bin"
1971#define LOG_FILE_FEAT_NUM_QUEUES"nvme_feature_setting_num_queues.bin" "nvme_feature_setting_num_queues.bin"
1972#define LOG_FILE_FEAT_IRQ_COAL"nvme_feature_setting_interrupt_coalescing.bin" "nvme_feature_setting_interrupt_coalescing.bin"
1973#define LOG_FILE_FEAT_IRQ_VECTOR"nvme_feature_setting_interrupt_vec_config.bin" "nvme_feature_setting_interrupt_vec_config.bin"
1974#define LOG_FILE_FEAT_WRITE_ATOMICITY"nvme_feature_setting_write_atomicity.bin" "nvme_feature_setting_write_atomicity.bin"
1975#define LOG_FILE_FEAT_ASYNC_EVENT"nvme_feature_setting_async_event_config.bin" "nvme_feature_setting_async_event_config.bin"
1976#define LOG_FILE_FEAT_SW_PROGRESS"nvme_feature_setting_sw_progress_marker.bin" "nvme_feature_setting_sw_progress_marker.bin"
1977
1978/*
1979 * Command info identifiers, keyed by the binary file each one describes. An
1980 * unrecognized name causes that log to be skipped.
1981 */
1982static const struct logpull_cmd_info {
1983 const char *cmd_info;
1984 __u8 opcode;
1985 __u8 log_id;
1986 __u16 cmd_class;
1987 __u16 cmd_code;
1988 const char *binary_file;
1989} logpull_cmd_info_table[] = {
1990 { "NVME_IDENTIFY_CONTROLLER", 0x06, 0, 0, 0, LOG_FILE_ID_CTRL"nvme_controller_identify_data.bin" },
1991 { "NVME_IDENTIFY_NAMESPACE_ALL", 0x06, 0, 0, 0, LOG_FILE_ID_NS"identify_namespace_%d_data.bin" },
1992 { "NVME_SMART_LOG", 0x02, 0x02, 0, 0, LOG_FILE_SMART"smart_data.bin" },
1993 { "NVME_ERROR_INFO_LOG", 0x02, 0x01, 0, 0, LOG_FILE_ERROR_INFO"error_information_log.bin" },
1994 { "NVME_FW_SLOT_INFO_LOG", 0x02, 0x03, 0, 0, LOG_FILE_FW_SLOT"firmware_slot_info_log.bin" },
1995 { "NVME_CHANGED_NAMESPACE_LIST_LOG", 0x02, 0x04, 0, 0, LOG_FILE_CHANGED_NS"changed_namespace_log.bin" },
1996 { "NVME_CMD_SUPPORTED_AND_EFFECTS_LOG", 0x02, 0x05, 0, 0, LOG_FILE_CMD_EFFECTS"command_effects_log.bin" },
1997 { "NVME_DEVICE_SELF_TEST_LOG", 0x02, 0x06, 0, 0, LOG_FILE_SELF_TEST"drive_self_test.bin" },
1998 { "NVME_HOST_TELEMETRY_LOG", 0x02, 0x07, 0, 0, LOG_FILE_TELEMETRY_HOST"nvme_host_telemetry_log.bin" },
1999 { "NVME_CTRL_TELEMETRY_LOG", 0x02, 0x08, 0, 0, LOG_FILE_TELEMETRY_CTRL"nvme_controller_telemetry_log.bin" },
2000 { "NVME_PERSISTENT_EVENT_LOG", 0x02, 0x0D, 0, 0, LOG_FILE_PEVENT"persistent_event_log.bin" },
2001 { "NVME_GET_FEATURE_ARBITRATION", 0, 0, 0, 0, LOG_FILE_FEAT_ARBITRATION"nvme_feature_setting_arbitration.bin" },
2002 { "NVME_GET_FEATURE_POWER_MGNT", 0, 0, 0, 0, LOG_FILE_FEAT_POWER_MGMT"nvme_feature_setting_pm.bin" },
2003 { "NVME_GET_FEATURE_LBA_RANGE_TYPE", 0, 0, 0, 0, LOG_FILE_FEAT_LBA_RANGE"nvme_feature_setting_lba_range_namespace_1.bin" },
2004 { "NVME_GET_FEATURE_TEMP_THRESHOLD", 0, 0, 0, 0, LOG_FILE_FEAT_TEMP_THRESHOLD"nvme_feature_setting_temp_threshold.bin" },
2005 { "NVME_GET_FEATURE_ERROR_RECOVERY", 0, 0, 0, 0, LOG_FILE_FEAT_ERROR_RECOVERY"nvme_feature_setting_error_recovery.bin" },
2006 { "NVME_GET_FEATURE_VOLATILE_WRITE_CACHE", 0, 0, 0, 0, LOG_FILE_FEAT_VWC"nvme_feature_setting_volatile_write_cache.bin" },
2007 { "NVME_GET_FEATURE_NUM_QUEUES", 0, 0, 0, 0, LOG_FILE_FEAT_NUM_QUEUES"nvme_feature_setting_num_queues.bin" },
2008 { "NVME_GET_FEATURE_INTERRUPT_COALESCING", 0, 0, 0, 0, LOG_FILE_FEAT_IRQ_COAL"nvme_feature_setting_interrupt_coalescing.bin" },
2009 { "NVME_GET_FEATURE_INTERRUPT_VECTOR_CONFIG", 0, 0, 0, 0, LOG_FILE_FEAT_IRQ_VECTOR"nvme_feature_setting_interrupt_vec_config.bin" },
2010 { "NVME_GET_FEATURE_WRITE_ATOMICITY", 0, 0, 0, 0, LOG_FILE_FEAT_WRITE_ATOMICITY"nvme_feature_setting_write_atomicity.bin" },
2011 { "NVME_GET_FEATURE_ASYNC_EVENT_CONFIG", 0, 0, 0, 0, LOG_FILE_FEAT_ASYNC_EVENT"nvme_feature_setting_async_event_config.bin" },
2012 { "NVME_GET_FEATURE_SW_PROGRESS_MARKER", 0, 0, 0, 0, LOG_FILE_FEAT_SW_PROGRESS"nvme_feature_setting_sw_progress_marker.bin" },
2013 { "VU_SMART_EXTENED_LOG", 0x02, 0xE1, 0, 0, LOG_FILE_VS_LOG(E1)"nvmelog_" "E1" ".bin" },
2014 { "MICRON_VS_LOG_D2", 0x02, 0xD2, 0, 0, LOG_FILE_VS_LOG(D2)"nvmelog_" "D2" ".bin" },
2015 { "MICRON_VS_LOG_E3", 0x02, 0xE3, 0, 0, LOG_FILE_VS_LOG(E3)"nvmelog_" "E3" ".bin" },
2016 { "MICRON_VS_LOG_E4", 0x02, 0xE4, 0, 0, LOG_FILE_VS_LOG(E4)"nvmelog_" "E4" ".bin" },
2017 { "MICRON_VS_LOG_E8", 0x02, 0xE8, 0, 0, LOG_FILE_VS_LOG(E8)"nvmelog_" "E8" ".bin" },
2018 { "MICRON_VS_LOG_E9", 0x02, 0xE9, 0, 0, LOG_FILE_VS_LOG(E9)"nvmelog_" "E9" ".bin" },
2019 { "MICRON_VS_LOG_EA", 0x02, 0xEA, 0, 0, LOG_FILE_VS_LOG(EA)"nvmelog_" "EA" ".bin" },
2020 { "MICRON_VS_LOG_FA", 0x02, 0xFA, 0, 0, LOG_FILE_VS_LOG(FA)"nvmelog_" "FA" ".bin" },
2021};
2022
2023/*
2024 * State for the cmd status CSV, set up once per debug-data collection.
2025 * Collection helpers are spread across several functions that don't share a
2026 * context argument, so the destination path and serial number are kept here
2027 * rather than threaded through every signature.
2028 */
2029static char logpull_csv_path[PATH_MAX4096];
2030static char logpull_csv_serial[sizeof(((struct nvme_id_ctrl *)0)->sn) + 1];
2031
2032/*
2033 * Tracks which table entries already have a Success row. Some log pages are
2034 * reachable from more than one collection table (LID 0x03, 0x05 and 0x06 are
2035 * collected generically and again as vendor log entries), and re-decoding the
2036 * same binary serves no purpose. A failed attempt is not recorded here, so a
2037 * later success on the same file still gets a row.
2038 */
2039static bool_Bool logpull_csv_logged[ARRAY_SIZE(logpull_cmd_info_table)(sizeof(logpull_cmd_info_table) / sizeof((logpull_cmd_info_table
)[0]) + 0)
];
2040
2041/*
2042 * Replace characters that would produce invalid JSON, in place. They are
2043 * substituted rather than escaped: these fields hold printable identifiers,
2044 * so anything else is already suspect, and malformed JSON would make the
2045 * whole metadata file unreadable.
2046 */
2047static char *sanitize_json_value(char *s)
2048{
2049 char *p;
2050
2051 for (p = s; *p; p++) {
2052 if (*p == '"' || *p == '\\' || !isprint((unsigned char)*p)((*__ctype_b_loc ())[(int) (((unsigned char)*p))] & (unsigned
short int) _ISprint)
)
2053 *p = '_';
2054 }
2055
2056 return s;
2057}
2058
2059/*
2060 * Create the cmd status CSV and write its header. Also records the serial
2061 * number and path used by LogCmdStatus(). Collection continues even if this
2062 * fails; the package is simply missing its log index.
2063 */
2064static void SetupCmdStatusLog(const char *ctrl_dir, const char *serial)
2065{
2066 FILE *fp = NULL((void*)0);
2067
2068 logpull_csv_path[0] = '\0';
2069 memset(logpull_csv_logged, 0, sizeof(logpull_csv_logged));
2070 snprintf(logpull_csv_serial, sizeof(logpull_csv_serial), "%s", serial);
2071
2072 if (snprintf(logpull_csv_path, sizeof(logpull_csv_path), "%s/%s",
2073 ctrl_dir, LOGPULL_CMD_STATUS_FILE"logpull_cmd_status_info.csv") >=
2074 (int)sizeof(logpull_csv_path)) {
2075 logpull_csv_path[0] = '\0';
2076 nvme_show_error("Path too long for %s", LOGPULL_CMD_STATUS_FILE)nvme_show_message(1, "Path too long for %s", "logpull_cmd_status_info.csv"
)
;
2077 return;
2078 }
2079
2080 fp = fopen(logpull_csv_path, "wb+");
2081 if (!fp) {
2082 nvme_show_error("Failed to create %s", logpull_csv_path)nvme_show_message(1, "Failed to create %s", logpull_csv_path);
2083 logpull_csv_path[0] = '\0';
2084 return;
2085 }
2086
2087 fprintf(fp,
2088 "serial_number,cmd_info,op_code,log_id,cmd_class,cmd_code,binary_file,execution_time_us,mse_status\n");
2089 fclose(fp);
2090}
2091
2092/*
2093 * Append a row to the cmd status CSV describing the collection of @file.
2094 *
2095 * @err is the result of the collection: 0 marks the log as available to
2096 * decode, any other value records the failure and excludes the log.
2097 * Files with no cmd_info identifier are skipped rather than listed under a
2098 * made-up name.
2099 *
2100 * execution_time_us is emitted as 0; the collection paths are not
2101 * instrumented for timing.
2102 */
2103static void LogCmdStatus(const char *file, int err)
2104{
2105 const struct logpull_cmd_info *info = NULL((void*)0);
2106 char status[256] = { 0 };
2107 FILE *fp = NULL((void*)0);
2108 size_t i;
2109
2110 if (!logpull_csv_path[0] || !file)
2111 return;
2112
2113 for (i = 0; i < ARRAY_SIZE(logpull_cmd_info_table)(sizeof(logpull_cmd_info_table) / sizeof((logpull_cmd_info_table
)[0]) + 0)
; i++) {
2114 if (!strcmp(file, logpull_cmd_info_table[i].binary_file)) {
2115 info = &logpull_cmd_info_table[i];
2116 break;
2117 }
2118 }
2119 if (!info || logpull_csv_logged[i])
2120 return;
2121 if (!err)
2122 logpull_csv_logged[i] = true1;
2123
2124 /*
2125 * Some status descriptions contain commas, which would split the last
2126 * cell across columns. Substitute them so the row keeps its 9 fields.
2127 */
2128 snprintf(status, sizeof(status), "%s",
2129 err ? libnvme_status_to_string(err, false0) : LOGPULL_STATUS_SUCCESS"Success");
2130 for (char *p = status; *p; p++) {
2131 if (*p == ',')
2132 *p = ';';
2133 }
2134
2135 fp = fopen(logpull_csv_path, "ab+");
2136 if (!fp)
2137 return;
2138
2139 fprintf(fp, "%s,%s,0x%.2X,0x%.2X,0x%.4X,0x%.4X,%s,%llu,%s\n",
2140 logpull_csv_serial, info->cmd_info, info->opcode, info->log_id,
2141 info->cmd_class, info->cmd_code, info->binary_file, 0ULL, status);
2142
2143 fclose(fp);
2144}
2145
2146/*
2147 * Write the log pull metadata JSON to the package root. This identifies the
2148 * drive the package was collected from.
2149 *
2150 * device_id and vendor_id must be correct for a valid decode.
2151 */
2152static void GetLogPullMetadata(const char *strOSDirName, struct nvme_id_ctrl *ctrlp,
2153 const char *tool_version)
2154{
2155 __cleanup_free__attribute__((cleanup(shr_freep))) char *tempFile = NULL((void*)0);
2156 __cleanup_free__attribute__((cleanup(shr_freep))) char *strPDir = NULL((void*)0);
2157 char model[sizeof(ctrlp->mn) + 1] = { 0 };
2158 char serial[sizeof(ctrlp->sn) + 1] = { 0 };
2159 char fwrev[sizeof(ctrlp->fr) + 1] = { 0 };
2160 char os_string[256] = { 0 };
2161 char timestamp[64] = { 0 };
2162 char utc_offset[16] = { 0 };
2163 char *mn, *sn, *fr;
2164 char *strDest = NULL((void*)0);
2165 FILE *fp = NULL((void*)0);
2166 struct tm *tmp;
2167 time_t t;
2168
2169 strPDir = strdup(strOSDirName);
2170 if (!strPDir) {
2171 nvme_show_error("Failed to allocate memory for directory name")nvme_show_message(1, "Failed to allocate memory for directory name"
)
;
2172 return;
2173 }
2174 strDest = dirname(strPDir);
2175
2176 if (asprintf(&tempFile, "%s/%s", strDest, LOGPULL_METADATA_FILE"logpull_metadata_info.json") < 0) {
2177 nvme_show_error("Failed to allocate memory for temp file name")nvme_show_message(1, "Failed to allocate memory for temp file name"
)
;
2178 return;
2179 }
2180
2181 /*
2182 * Identify strings are space-padded and not NUL-terminated; copy them
2183 * out and trim any whitespace.
2184 */
2185 snprintf(model, sizeof(model), "%-.*s", (int)sizeof(ctrlp->mn), ctrlp->mn);
2186 snprintf(serial, sizeof(serial), "%-.*s", (int)sizeof(ctrlp->sn), ctrlp->sn);
2187 snprintf(fwrev, sizeof(fwrev), "%-.*s", (int)sizeof(ctrlp->fr), ctrlp->fr);
2188
2189 mn = sanitize_json_value(shr_trim(model));
2190 sn = sanitize_json_value(shr_trim(serial));
2191 fr = sanitize_json_value(shr_trim(fwrev));
2192
2193 micron_get_os_string(os_string, sizeof(os_string));
2194 sanitize_json_value(os_string);
2195
2196 t = time(NULL((void*)0));
2197 tmp = localtime(&t);
2198 if (tmp) {
2199 char zone[8] = { 0 };
2200
2201 strftime(timestamp, sizeof(timestamp), "%a %b %d %H:%M:%S %Y", tmp);
2202 /* %z gives "+hhmm"; this field is written as "+hh:mm" */
2203 if (strftime(zone, sizeof(zone), "%z", tmp) == 5)
2204 snprintf(utc_offset, sizeof(utc_offset), "%.3s:%.2s",
2205 zone, zone + 3);
2206 else
2207 snprintf(utc_offset, sizeof(utc_offset), "%s", zone);
2208 }
2209
2210 fp = fopen(tempFile, "wb+");
2211 if (!fp) {
2212 nvme_show_error("Failed to create %s", tempFile)nvme_show_message(1, "Failed to create %s", tempFile);
2213 return;
2214 }
2215
2216 fprintf(fp, "{\n");
2217 fprintf(fp, " \"system_os\": \"%s\",\n", os_string);
2218 fprintf(fp, " \"logpull_timestamp\": \"%s\",\n", timestamp);
2219 fprintf(fp, " \"utc_offset\": \"%s\",\n", utc_offset);
2220 fprintf(fp, " \"package_version\": \"%s\",\n", LOGPULL_PACKAGE_VERSION"1.4");
2221 fprintf(fp, " \"tool_pull_name\": \"nvme-cli\",\n");
2222 fprintf(fp, " \"tool_pull_version\": \"%s\",\n", tool_version);
2223 fprintf(fp, " \"vendor_id\": \"0x%04X\",\n", vendor_id);
2224 fprintf(fp, " \"device_id\": \"0x%04X\",\n", device_id);
2225 fprintf(fp, " \"serial_number\": \"%s\",\n", sn);
2226 fprintf(fp, " \"model_number_identify\": \"%s\",\n", mn);
2227 fprintf(fp, " \"firmware_revision\": \"%s\"\n", fr);
2228 fprintf(fp, "}\n");
2229
2230 fclose(fp);
2231}
2232
2233static void GetDriveInfo(const char *strOSDirName, int nFD,
2234 struct nvme_id_ctrl *ctrlp)
2235{
2236 FILE *fpOutFile = NULL((void*)0);
2237 __cleanup_free__attribute__((cleanup(shr_freep))) char *tempFile = NULL((void*)0);
2238 char strBuffer[1024] = { 0 };
2239 char model[sizeof(ctrlp->mn) + 1] = { 0 };
2240 char serial[sizeof(ctrlp->sn) + 1] = { 0 };
2241 char fwrev[sizeof(ctrlp->fr) + 1] = { 0 };
2242 __cleanup_free__attribute__((cleanup(shr_freep))) char *strPDir = NULL((void*)0);
2243 char *strDest = NULL((void*)0);
2244
2245 strPDir = strdup(strOSDirName);
2246 if (!strPDir) {
2247 nvme_show_error("Failed to allocate memory for directory name")nvme_show_message(1, "Failed to allocate memory for directory name"
)
;
2248 return;
2249 }
2250 strDest = dirname(strPDir);
2251
2252 if (asprintf(&tempFile, "%s/%s", strDest, "drive-info.txt") < 0) {
2253 nvme_show_error("Failed to allocate memory for temp file name")nvme_show_message(1, "Failed to allocate memory for temp file name"
)
;
2254 return;
2255 }
2256
2257 fpOutFile = fopen(tempFile, "wb+");
2258 if (!fpOutFile) {
2259 nvme_show_error("Failed to create %s", tempFile)nvme_show_message(1, "Failed to create %s", tempFile);
2260 return;
2261 }
2262
2263 memcpy(model, ctrlp->mn, sizeof(ctrlp->mn));
2264 memcpy(serial, ctrlp->sn, sizeof(ctrlp->sn));
2265 memcpy(fwrev, ctrlp->fr, sizeof(ctrlp->fr));
2266
2267 snprintf(strBuffer, sizeof(strBuffer),
2268 "********************\nDrive Info\n********************\n");
2269
2270 fprintf(fpOutFile, "%s", strBuffer);
2271 snprintf(strBuffer, sizeof(strBuffer),
2272 "%-20s : /dev/nvme%d\n%-20s : %s\n%-20s : %-20s\n%-20s : %-20s\n",
2273 "Device Name", nFD,
2274 "Model No", (char *)model,
2275 "Serial No", (char *)serial, "FW-Rev", (char *)fwrev);
2276
2277 fprintf(fpOutFile, "%s", strBuffer);
2278
2279 snprintf(strBuffer, sizeof(strBuffer),
2280 "\n********************\nPCI Info\n********************\n");
2281
2282 fprintf(fpOutFile, "%s", strBuffer);
2283
2284 snprintf(strBuffer, sizeof(strBuffer),
2285 "%-22s : %04X\n%-22s : %04X\n",
2286 "VendorId", vendor_id, "DeviceId", device_id);
2287 fprintf(fpOutFile, "%s", strBuffer);
2288 fclose(fpOutFile);
2289}
2290
2291static void GetTimestampInfo(const char *strOSDirName)
2292{
2293 __u8 outstr[1024];
2294 time_t t;
2295 struct tm *tmp;
2296 size_t num;
2297 size_t remaining;
2298 int n;
2299 __cleanup_free__attribute__((cleanup(shr_freep))) char *strPDir = NULL((void*)0);
2300 char *strDest = NULL((void*)0);
2301
2302 t = time(NULL((void*)0));
2303 tmp = localtime(&t);
2304 if (!tmp)
1
Assuming 'tmp' is non-null
2
Taking false branch
2305 return;
2306
2307 num = strftime((char *)outstr, sizeof(outstr),
2308 "Timestamp (UTC): %a, %d %b %Y %H:%M:%S %z", tmp);
2309 remaining = sizeof(outstr) - num;
2310 n = snprintf((char *)(outstr + num), remaining, "\nPackage Version: 1.4");
2311 if (n > 0)
3
Assuming 'n' is <= 0
4
Taking false branch
2312 num += (size_t)n < remaining ? (size_t)n : remaining - 1;
2313 if (num) {
5
Assuming 'num' is not equal to 0
6
Taking true branch
2314 strPDir = strdup(strOSDirName);
7
Memory is allocated
2315 if (!strPDir)
8
Assuming 'strPDir' is non-null
9
Taking false branch
2316 return;
2317 strDest = dirname(strPDir);
10
Potential leak of memory pointed to by 'strPDir'
2318 WriteData(outstr, num, strDest, "timestamp_info.txt", "timestamp");
2319 }
2320}
2321
2322static void GetCtrlIDDInfo(const char *dir, struct nvme_id_ctrl *ctrlp)
2323{
2324 WriteData((__u8 *)ctrlp, sizeof(*ctrlp), dir,
2325 LOG_FILE_ID_CTRL"nvme_controller_identify_data.bin", "id-ctrl");
2326 LogCmdStatus(LOG_FILE_ID_CTRL"nvme_controller_identify_data.bin", 0);
2327}
2328
2329static void GetSmartlogData(struct libnvme_transport_handle *hdl, const char *dir)
2330{
2331 struct nvme_smart_log smart_log;
2332 int err;
2333
2334 err = nvme_get_log_smart(hdl, NVME_NSID_ALL, &smart_log);
2335 if (!err)
2336 WriteData((__u8 *)&smart_log, sizeof(smart_log), dir,
2337 LOG_FILE_SMART"smart_data.bin", "smart log");
2338 LogCmdStatus(LOG_FILE_SMART"smart_data.bin", err);
2339}
2340
2341static void GetErrorlogData(struct libnvme_transport_handle *hdl, int entries, const char *dir)
2342{
2343 int logSize = entries * sizeof(struct nvme_error_log_page);
2344 __cleanup_libnvme_free__attribute__((cleanup(libnvme_freep))) struct nvme_error_log_page *error_log =
2345 (struct nvme_error_log_page *)libnvme_alloc(logSize);
2346 struct libnvme_passthru_cmd cmd;
2347 size_t len;
2348 int err;
2349
2350 if (!error_log)
2351 return;
2352
2353 len = sizeof(*error_log) * entries;
2354
2355 nvme_init_get_log(&cmd, NVME_NSID_ALL, NVME_LOG_LID_ERROR,
2356 NVME_CSI_NVM, error_log, len);
2357
2358 err = libnvme_get_log(hdl, &cmd, false0, len);
2359 if (!err)
2360 WriteData((__u8 *)error_log, logSize, dir,
2361 LOG_FILE_ERROR_INFO"error_information_log.bin", "error log");
2362 LogCmdStatus(LOG_FILE_ERROR_INFO"error_information_log.bin", err);
2363}
2364
2365static void GetGenericLogs(struct libnvme_transport_handle *hdl, const char *dir)
2366{
2367 struct nvme_self_test_log self_test_log;
2368 struct nvme_firmware_slot fw_log;
2369 struct nvme_cmd_effects_log effects;
2370 struct nvme_persistent_event_log pevent_log;
2371 __cleanup_huge__attribute__((cleanup(libnvme_free_huge))) struct libnvme_mem_huge mh = { 0, };
2372 void *pevent_log_info = NULL((void*)0);
2373 __u32 log_len = 0;
2374 int err = 0;
2375 struct libnvme_passthru_cmd cmd;
2376 size_t len;
2377
2378 /* get self test log */
2379 len = sizeof(self_test_log);
2380 nvme_init_get_log(&cmd, NVME_NSID_ALL, NVME_LOG_LID_DEVICE_SELF_TEST,
2381 NVME_CSI_NVM, &self_test_log, len);
2382 err = libnvme_get_log(hdl, &cmd, false0, len);
2383 if (!err)
2384 WriteData((__u8 *)&self_test_log, len, dir,
2385 LOG_FILE_SELF_TEST"drive_self_test.bin", "self test log");
2386 LogCmdStatus(LOG_FILE_SELF_TEST"drive_self_test.bin", err);
2387
2388 /* get fw slot info log */
2389 len = sizeof(fw_log);
2390 nvme_init_get_log(&cmd, NVME_NSID_ALL, NVME_LOG_LID_FW_SLOT,
2391 NVME_CSI_NVM, &fw_log, len);
2392 err = libnvme_get_log(hdl, &cmd, false0, len);
2393 if (!err)
2394 WriteData((__u8 *)&fw_log, len, dir,
2395 LOG_FILE_FW_SLOT"firmware_slot_info_log.bin", "firmware log");
2396 LogCmdStatus(LOG_FILE_FW_SLOT"firmware_slot_info_log.bin", err);
2397
2398 /* get effects log */
2399 len = sizeof(effects);
2400 nvme_init_get_log_cmd_effects(&cmd, NVME_CSI_NVM, &effects);
2401 err = libnvme_get_log(hdl, &cmd, false0, len);
2402 if (!err)
2403 WriteData((__u8 *)&effects, len, dir,
2404 LOG_FILE_CMD_EFFECTS"command_effects_log.bin", "effects log");
2405 LogCmdStatus(LOG_FILE_CMD_EFFECTS"command_effects_log.bin", err);
2406
2407 /* get persistent event log */
2408 (void)nvme_get_log_persistent_event(hdl, NVME_PEVENT_LOG_RELEASE_CTX,
2409 &pevent_log, sizeof(pevent_log));
2410 memset(&pevent_log, 0, sizeof(pevent_log));
2411 err = nvme_get_log_persistent_event(hdl, NVME_PEVENT_LOG_EST_CTX_AND_READ,
2412 &pevent_log, sizeof(pevent_log));
2413 if (err) {
2414 nvme_show_error("Setting persistent event log read ctx failed (ignored)!")nvme_show_message(1, "Setting persistent event log read ctx failed (ignored)!"
)
;
2415 return;
2416 }
2417
2418 log_len = le64_to_cpu(pevent_log.tll);
2419 pevent_log_info = libnvme_alloc_huge(log_len, &mh);
2420 if (!pevent_log_info) {
2421 nvme_show_perror("could not alloc buffer for persistent event log page (ignored)!\n");
2422 return;
2423 }
2424
2425 err = nvme_get_log_persistent_event(hdl, NVME_PEVENT_LOG_READ,
2426 pevent_log_info, log_len);
2427 if (!err)
2428 WriteData((__u8 *)pevent_log_info, log_len, dir,
2429 LOG_FILE_PEVENT"persistent_event_log.bin", "persistent event log");
2430 LogCmdStatus(LOG_FILE_PEVENT"persistent_event_log.bin", err);
2431}
2432
2433static int GetNSIDDInfo(struct libnvme_transport_handle *hdl, const char *dir, int nsid)
2434{
2435 char file[PATH_MAX4096] = { 0 };
2436 struct nvme_id_ns ns;
2437 struct libnvme_passthru_cmd cmd;
2438 int err;
2439
2440 nvme_init_identify_ns(&cmd, nsid, &ns);
2441 err = libnvme_exec_admin_passthru(hdl, &cmd);
2442 if (!err) {
2443 snprintf(file, sizeof(file), LOG_FILE_ID_NS"identify_namespace_%d_data.bin", nsid);
2444 WriteData((__u8 *)&ns, sizeof(ns), dir, file, "id-ns");
2445 }
2446
2447 return err;
2448}
2449
2450static void GetOSConfig(const char *strOSDirName)
2451{
2452 __cleanup_free__attribute__((cleanup(shr_freep))) char *strFileName = NULL((void*)0);
2453
2454 if (asprintf(&strFileName, "%s/%s", strOSDirName, "os_config.txt") < 0)
2455 return;
2456 micron_write_os_config_to_file(strFileName);
2457}
2458
2459static int micron_telemetry_log(struct libnvme_transport_handle *hdl, __u8 type, __u8 **data,
2460 uint32_t *logSize, int da)
2461{
2462 struct libnvme_passthru_cmd cmd;
2463 int err;
2464 int bs = NVME_LOG_TELEM_BLOCK_SIZE;
2465 uint32_t dalb = 0;
2466 bool_Bool ctrl_init = (type == NVME_LOG_LID_TELEMETRY_CTRL);
2467 struct nvme_telemetry_log *log = libnvme_alloc(bs);
2468 void *p;
2469
2470 if (!log) {
2471 nvme_show_error("Failed to allocate memory for %s telemetry log header",nvme_show_message(1, "Failed to allocate memory for %s telemetry log header"
, ctrl_init ? "controller" : "host")
2472 ctrl_init ? "controller" : "host")nvme_show_message(1, "Failed to allocate memory for %s telemetry log header"
, ctrl_init ? "controller" : "host")
;
2473 return -ENOMEM12;
2474 }
2475
2476 if (ctrl_init) {
2477 nvme_init_get_log_telemetry_ctrl(&cmd, 0, log, bs);
2478 err = libnvme_get_log_dynamic_chunk(hdl, &cmd, true1, bs);
2479 } else {
2480 nvme_init_get_log_create_telemetry_host(&cmd, log);
2481 err = libnvme_get_log_dynamic_chunk(hdl, &cmd, false0, bs);
2482 }
2483
2484 if (err) {
2485 nvme_show_error("Failed to get telemetry log header for %s",nvme_show_message(1, "Failed to get telemetry log header for %s"
, ctrl_init ? "controller" : "host")
2486 ctrl_init ? "controller" : "host")nvme_show_message(1, "Failed to get telemetry log header for %s"
, ctrl_init ? "controller" : "host")
;
2487 libnvme_free(log);
2488 return err;
2489 }
2490
2491 switch (da) {
2492 case 1:
2493 dalb = le16_to_cpu(log->dalb1);
2494 break;
2495 case 2:
2496 dalb = le16_to_cpu(log->dalb2);
2497 break;
2498 case 3:
2499 dalb = le16_to_cpu(log->dalb3);
2500 break;
2501 case 4:
2502 dalb = le32_to_cpu(log->dalb4);
2503 break;
2504 default:
2505 nvme_show_error("Invalid data area: %d", da)nvme_show_message(1, "Invalid data area: %d", da);
2506 libnvme_free(log);
2507 return -EINVAL22;
2508 }
2509
2510 if (!dalb) {
2511 nvme_show_error("Requested telemetry data for %s data area %d is empty",nvme_show_message(1, "Requested telemetry data for %s data area %d is empty"
, ctrl_init ? "controller" : "host", da)
2512 ctrl_init ? "controller" : "host", da)nvme_show_message(1, "Requested telemetry data for %s data area %d is empty"
, ctrl_init ? "controller" : "host", da)
;
2513 libnvme_free(log);
2514 return -1;
2515 }
2516
2517 *logSize = (dalb + 1) * bs;
2518 p = libnvme_realloc(log, (size_t)(*logSize));
2519 if (!p) {
2520 nvme_show_error("Failed to allocate memory for %s telemetry data (%u bytes)",nvme_show_message(1, "Failed to allocate memory for %s telemetry data (%u bytes)"
, ctrl_init ? "controller" : "host", *logSize)
2521 ctrl_init ? "controller" : "host", *logSize)nvme_show_message(1, "Failed to allocate memory for %s telemetry data (%u bytes)"
, ctrl_init ? "controller" : "host", *logSize)
;
2522 libnvme_free(log);
2523 return -ENOMEM12;
2524 }
2525 log = p;
2526
2527 if (ctrl_init) {
2528 nvme_init_get_log_telemetry_ctrl(&cmd, bs, (__u8 *)log + bs, *logSize - bs);
2529 err = libnvme_get_log_dynamic_chunk(hdl, &cmd, true1, *logSize - bs);
2530 } else {
2531 nvme_init_get_log_telemetry_host(&cmd, bs, (__u8 *)log + bs, *logSize - bs);
2532 err = libnvme_get_log_dynamic_chunk(hdl, &cmd, false0, *logSize - bs);
2533 }
2534
2535 if (!err) {
2536 *data = (__u8 *)log;
2537 } else {
2538 nvme_show_err(err, "Failed to get telemetry data for %s\n",
2539 ctrl_init ? "controller" : "host");
2540 libnvme_free(log);
2541 }
2542
2543 return err;
2544}
2545
2546static int GetTelemetryData(struct libnvme_transport_handle *hdl,
2547 const char *dir, bool_Bool da4_support)
2548{
2549 unsigned char *buffer = NULL((void*)0);
2550 int i, err;
2551 uint32_t logSize = 0;
2552 char msg[256] = { 0 };
2553 struct {
2554 __u8 log;
2555 char *file;
2556 } tmap[] = {
2557 {NVME_LOG_LID_TELEMETRY_HOST, LOG_FILE_TELEMETRY_HOST"nvme_host_telemetry_log.bin"},
2558 {NVME_LOG_LID_TELEMETRY_CTRL, LOG_FILE_TELEMETRY_CTRL"nvme_controller_telemetry_log.bin"},
2559 };
2560
2561 for (i = 0; i < (int)(ARRAY_SIZE(tmap)(sizeof(tmap) / sizeof((tmap)[0]) + 0)); i++) {
2562 err = micron_telemetry_log(hdl, tmap[i].log, &buffer, &logSize,
2563 da4_support ? 4 : 3);
2564 if (!err && logSize > 0 && buffer) {
2565 snprintf(msg, sizeof(msg), "telemetry log: 0x%X", tmap[i].log);
2566 WriteData(buffer, logSize, dir, tmap[i].file, msg);
2567 }
2568 LogCmdStatus(tmap[i].file, err);
2569 libnvme_free(buffer);
2570 buffer = NULL((void*)0);
2571 logSize = 0;
2572 }
2573 return err;
2574}
2575
2576static int GetFeatureSettings(struct libnvme_transport_handle *hdl, const char *dir)
2577{
2578 unsigned char *bufp, buf[4096] = { 0 };
2579 int i, err, len, errcnt = 0;
2580 __u64 attrVal = 0;
2581 char msg[256] = { 0 };
2582
2583 struct features {
2584 int id;
2585 char *file;
2586 } fmap[] = {
2587 {0x01, LOG_FILE_FEAT_ARBITRATION"nvme_feature_setting_arbitration.bin"},
2588 {0x02, LOG_FILE_FEAT_POWER_MGMT"nvme_feature_setting_pm.bin"},
2589 {0x03, LOG_FILE_FEAT_LBA_RANGE"nvme_feature_setting_lba_range_namespace_1.bin"},
2590 {0x04, LOG_FILE_FEAT_TEMP_THRESHOLD"nvme_feature_setting_temp_threshold.bin"},
2591 {0x05, LOG_FILE_FEAT_ERROR_RECOVERY"nvme_feature_setting_error_recovery.bin"},
2592 {0x06, LOG_FILE_FEAT_VWC"nvme_feature_setting_volatile_write_cache.bin"},
2593 {0x07, LOG_FILE_FEAT_NUM_QUEUES"nvme_feature_setting_num_queues.bin"},
2594 {0x08, LOG_FILE_FEAT_IRQ_COAL"nvme_feature_setting_interrupt_coalescing.bin"},
2595 {0x09, LOG_FILE_FEAT_IRQ_VECTOR"nvme_feature_setting_interrupt_vec_config.bin"},
2596 {0x0A, LOG_FILE_FEAT_WRITE_ATOMICITY"nvme_feature_setting_write_atomicity.bin"},
2597 {0x0B, LOG_FILE_FEAT_ASYNC_EVENT"nvme_feature_setting_async_event_config.bin"},
2598 {0x80, LOG_FILE_FEAT_SW_PROGRESS"nvme_feature_setting_sw_progress_marker.bin"},
2599 };
2600
2601 for (i = 0; i < (int)(ARRAY_SIZE(fmap)(sizeof(fmap) / sizeof((fmap)[0]) + 0)); i++) {
2602 if (fmap[i].id == 0x03) {
2603 len = 4096;
2604 bufp = (unsigned char *)(&buf[0]);
2605 } else {
2606 len = 0;
2607 bufp = NULL((void*)0);
2608 }
2609 err = nvme_get_features(hdl, 1, fmap[i].id, 0, 0x0, 0, bufp, len,
2610 &attrVal);
2611 if (!err) {
2612 snprintf(msg, sizeof(msg), "feature: 0x%X", fmap[i].id);
2613 WriteData((__u8 *)&attrVal, sizeof(attrVal), dir, fmap[i].file, msg);
2614 if (bufp)
2615 WriteData(bufp, len, dir, fmap[i].file, msg);
2616 } else {
2617 nvme_show_error("Feature 0x%x data not retrieved, error %d (ignored)!",nvme_show_message(1, "Feature 0x%x data not retrieved, error %d (ignored)!"
, fmap[i].id, err)
2618 fmap[i].id, err)nvme_show_message(1, "Feature 0x%x data not retrieved, error %d (ignored)!"
, fmap[i].id, err)
;
2619 errcnt++;
2620 }
2621 LogCmdStatus(fmap[i].file, err);
2622 }
2623 return (int)(errcnt == ARRAY_SIZE(fmap)(sizeof(fmap) / sizeof((fmap)[0]) + 0));
2624}
2625
2626static int micron_drive_info(int argc, char **argv, struct command *acmd,
2627 struct plugin *plugin)
2628{
2629 const char *desc = "Get drive HW information";
2630 struct nvme_id_ctrl ctrl = { 0 };
2631 struct libnvme_passthru_cmd admin_cmd = { 0 };
2632 struct libnvme_passthru_cmd cmd;
2633 unsigned char logC0[C0_log_size512] = { 0 };
2634 struct fb_drive_info {
2635 unsigned char hw_ver_major;
2636 unsigned char hw_ver_minor;
2637 unsigned char ftl_unit_size;
2638 unsigned short bs_ver_major;
2639 unsigned short bs_ver_minor;
2640 unsigned int ownership_status;
2641 } dinfo = { 0 };
2642 enum eDriveModel model = UNKNOWN_MODEL;
2643 __u8 custId = 0x10; /* default Micron generic */
2644 bool_Bool is_json = false0;
2645 struct json_object *root;
2646 struct json_object *driveInfo;
2647 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
2648 __cleanup_nvme_transport_handle__attribute__((cleanup(cleanup_nvme_transport_handle))) struct libnvme_transport_handle *hdl = NULL((void*)0);
2649 int err = 0;
2650
2651 nvme_print_flags_t format;
2652
2653 OPT_ARGS(opts)struct argconfig_commandline_options opts[] = {
2654 OPT_END(){ ((void*)0) }
2655 };
2656
2657 err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &model);
2658 if (err)
2659 return err;
2660
2661 if (model == UNKNOWN_MODEL) {
2662 nvme_show_error("ERROR : Unsupported drive for vs-drive-info cmd")nvme_show_message(1, "ERROR : Unsupported drive for vs-drive-info cmd"
)
;
2663 return -1;
2664 }
2665
2666 format = nvme_args.output_flags;
2667 is_json = format == JSON;
2668
2669 if (model == M5407) {
2670 admin_cmd.opcode = 0xDA;
2671 admin_cmd.addr = (__u64) (uintptr_t) &dinfo;
2672 admin_cmd.data_len = (__u32)sizeof(dinfo);
2673 admin_cmd.cdw12 = 3;
2674 err = libnvme_exec_admin_passthru(hdl, &admin_cmd);
2675 if (err) {
2676 nvme_show_error("ERROR : drive-info opcode failed with 0x%x", err)nvme_show_message(1, "ERROR : drive-info opcode failed with 0x%x"
, err)
;
2677 return -1;
2678 }
2679 } else {
2680 nvme_init_identify_ctrl(&cmd, &ctrl);
2681 err = libnvme_exec_admin_passthru(hdl, &cmd);
2682 if (err) {
2683 nvme_show_error("ERROR : identify_ctrl() failed with 0x%x", err)nvme_show_message(1, "ERROR : identify_ctrl() failed with 0x%x"
, err)
;
2684 return -1;
2685 }
2686 dinfo.hw_ver_major = ctrl.vs[820];
2687 dinfo.hw_ver_minor = ctrl.vs[821];
2688 dinfo.ftl_unit_size = ctrl.vs[822];
2689 custId = ctrl.vs[536];
2690 }
2691
2692 if ((custId == MICRON_CUST_ID_GG0x16) && (model == M51CX)) {
2693 err = nvme_get_log_simple(hdl, 0xC0, logC0, C0_log_size512);
2694 if (err == 0) {
2695 dinfo.bs_ver_major = *((__u16 *)(logC0+300));
2696 dinfo.bs_ver_minor = *((__u16 *)(logC0+302));
2697 dinfo.ownership_status = *((__u32 *)(logC0+312));
2698 } else {
2699 nvme_show_err(err, "Unable to retrieve extended smart log 0xC0 for the drive");
2700 return -1;
2701 }
2702 }
2703
2704 if (is_json) {
2705 struct json_object *pinfo = json_create_object()json_object_new_object();
2706 char tempstr[64] = { 0 };
2707 root = json_create_object()json_object_new_object();
2708 driveInfo = json_create_array()json_object_new_array();
2709 json_object_add_value_array(root, "Micron Drive HW Information", driveInfo)json_object_object_add(root, "Micron Drive HW Information", driveInfo
)
;
2710
2711 snprintf(tempstr, sizeof(tempstr), "%hhu.%hhu",
2712 dinfo.hw_ver_major, dinfo.hw_ver_minor);
2713 json_object_add_value_string(pinfo, "Drive Hardware Version", tempstr);
2714
2715 if (custId == MICRON_CUST_ID_GG0x16) {
2716 if (dinfo.ftl_unit_size) {
2717 snprintf(tempstr, sizeof(tempstr), "%u B",
2718 dinfo.ftl_unit_size * 1024U);
2719 json_object_add_value_string(pinfo, "FTL_unit_size", tempstr);
2720 }
2721
2722 if (dinfo.bs_ver_major != 0 || dinfo.bs_ver_minor != 0) {
2723 snprintf(tempstr, sizeof(tempstr),
2724 "HyperScale Boot Version Spec.%hhu.%hhu",
2725 (unsigned char)dinfo.bs_ver_major,
2726 (unsigned char)dinfo.bs_ver_minor);
2727 json_object_add_value_string(pinfo, "Boot Spec.Version", tempstr);
2728 }
2729
2730 if (dinfo.ownership_status == 0)
2731 snprintf(tempstr, sizeof(tempstr), "N/A");
2732 else if (dinfo.ownership_status == 1)
2733 snprintf(tempstr, sizeof(tempstr), "UNSET");
2734 else if (dinfo.ownership_status == 2)
2735 snprintf(tempstr, sizeof(tempstr), "SET");
2736 else if (dinfo.ownership_status == 3)
2737 snprintf(tempstr, sizeof(tempstr), "BLOCKED");
2738 json_object_add_value_string(pinfo, "Drive Ownership Status", tempstr);
2739
2740 } else {
2741 if (dinfo.ftl_unit_size) {
2742 snprintf(tempstr, sizeof(tempstr), "%hhu KB",
2743 dinfo.ftl_unit_size);
2744 json_object_add_value_string(pinfo, "FTL_unit_size", tempstr);
2745 }
2746 if (dinfo.bs_ver_major != 0 || dinfo.bs_ver_minor != 0) {
2747 snprintf(tempstr, sizeof(tempstr), "%hhu.%hhu",
2748 (unsigned char)dinfo.bs_ver_major,
2749 (unsigned char)dinfo.bs_ver_minor);
2750 json_object_add_value_string(pinfo, "Boot Spec.Version", tempstr);
2751 }
2752 }
2753 json_array_add_value_object(driveInfo, pinfo)json_object_array_add(driveInfo, pinfo);
2754 json_print_object(root, NULL)printf("%s", json_object_to_json_string_ext(root, (1 <<
1) | (1 << 4)))
;
2755 printf("\n");
2756 json_free_object(root)json_object_put(root);
2757 } else {
2758 printf("Drive Hardware Version: %hhu.%hhu\n",
2759 dinfo.hw_ver_major, dinfo.hw_ver_minor);
2760
2761 if (custId == MICRON_CUST_ID_GG0x16) {
2762 if (dinfo.ftl_unit_size)
2763 printf("FTL_unit_size: %u B\n",
2764 (unsigned int)(dinfo.ftl_unit_size * 1024));
2765
2766 if (dinfo.bs_ver_major != 0 || dinfo.bs_ver_minor != 0) {
2767 printf(
2768 "Boot Spec.Version: HyperScale Boot Version Spec.%hhu.%hhu\n"
2769 , (unsigned char)dinfo.bs_ver_major,
2770 (unsigned char)dinfo.bs_ver_minor);
2771 }
2772
2773 if (dinfo.ownership_status == 0)
2774 printf("Drive Ownership Status: N/A\n");
2775 else if (dinfo.ownership_status == 1)
2776 printf("Drive Ownership Status: UNSET\n");
2777 else if (dinfo.ownership_status == 2)
2778 printf("Drive Ownership Status: SET\n");
2779 else if (dinfo.ownership_status == 3)
2780 printf("Drive Ownership Status: BLOCKED\n");
2781
2782 } else {
2783 if (dinfo.ftl_unit_size)
2784 printf("FTL_unit_size: %hhu KB\n", dinfo.ftl_unit_size);
2785 if (dinfo.bs_ver_major != 0 || dinfo.bs_ver_minor != 0)
2786 printf(
2787 "Boot Spec.Version: %hhu.%hhu\n"
2788 , (unsigned char)dinfo.bs_ver_major,
2789 (unsigned char)dinfo.bs_ver_minor);
2790 }
2791
2792 }
2793
2794 return 0;
2795}
2796
2797static int micron_cloud_ssd_plugin_version(int argc, char **argv,
2798 struct command *command, struct plugin *plugin)
2799{
2800 const char *desc = "Prints the Micron cloud SSD plugin version.";
2801 int err;
2802
2803 OPT_ARGS(opts)struct argconfig_commandline_options opts[] = {
2804 OPT_END(){ ((void*)0) }
2805 };
2806
2807 err = parse_args(argc, argv, desc, opts);
2808 if (err)
2809 return err;
2810
2811 nvme_show_result("nvme-cli Micron cloud SSD plugin version: %s.%s",nvme_show_message(0, "nvme-cli Micron cloud SSD plugin version: %s.%s"
, __version_major, __version_minor)
2812 __version_major, __version_minor)nvme_show_message(0, "nvme-cli Micron cloud SSD plugin version: %s.%s"
, __version_major, __version_minor)
;
2813 return 0;
2814}
2815
2816static int micron_plugin_version(int argc, char **argv, struct command *acmd,
2817 struct plugin *plugin)
2818{
2819 const char *desc = "Prints the Micron plugin version.";
2820 int err;
2821
2822 OPT_ARGS(opts)struct argconfig_commandline_options opts[] = {
2823 OPT_END(){ ((void*)0) }
2824 };
2825
2826 err = parse_args(argc, argv, desc, opts);
2827 if (err)
2828 return err;
2829
2830 nvme_show_result("nvme-cli Micron plugin version: %s.%s.%s",nvme_show_message(0, "nvme-cli Micron plugin version: %s.%s.%s"
, __version_major, __version_minor, __version_patch)
2831 __version_major, __version_minor, __version_patch)nvme_show_message(0, "nvme-cli Micron plugin version: %s.%s.%s"
, __version_major, __version_minor, __version_patch)
;
2832 return 0;
2833}
2834
2835/* Binary format of firmware activation history entry */
2836struct __packed__attribute__((__packed__)) fw_activation_history_entry {
2837 __u8 version;
2838 __u8 length;
2839 __u16 rsvd1;
2840 __le16 valid;
2841 __le64 power_on_hour;
2842 __le64 rsvd2;
2843 __le64 power_cycle_count;
2844 __u8 previous_fw[8];
2845 __u8 activated_fw[8];
2846 __u8 slot;
2847 __u8 commit_action_type;
2848 __le16 result;
2849 __u8 rsvd3[14];
2850};
2851
2852/* Binary format for firmware activation history table */
2853struct __packed__attribute__((__packed__)) micron_fw_activation_history_table {
2854 __u8 log_page;
2855 __u8 rsvd1[3];
2856 __le32 num_entries;
2857 struct fw_activation_history_entry entries[20];
2858 __u8 rsvd2[2790];
2859 __u16 version;
2860 __u8 GUID[16];
2861};
2862
2863static int display_fw_activate_entry(int entry_count, struct fw_activation_history_entry *entry,
2864 char *formatted_entry, size_t buf_size,
2865 struct json_object *stats)
2866{
2867 time_t timestamp, hours;
2868 char buffer[32];
2869 __u8 minutes, seconds;
2870 static const char * const ca[] = {"000b", "001b", "010b", "011b"};
2871 char *ptr = formatted_entry;
2872 int index = 0, entry_size = 82;
2873 int remaining;
2874 bool_Bool is_json = false0;
2875
2876 if ((entry->version != 1 && entry->version != 2) || entry->length != 64)
2877 return -EINVAL22;
2878
2879 if (stats)
2880 is_json = true1;
2881
2882 remaining = buf_size - (ptr - formatted_entry);
2883 snprintf(ptr, remaining, "%d", entry_count);
2884 if (is_json)
2885 json_object_add_value_int(stats, "Entry Number", le32_to_cpu(entry_count))json_object_object_add(stats, "Entry Number", json_object_new_int
(le32_to_cpu(entry_count)))
;
2886
2887 ptr += 10;
2888
2889 timestamp = (le64_to_cpu(entry->power_on_hour) & 0x0000FFFFFFFFFFFFUL) / 1000;
2890 hours = timestamp / 3600;
2891 minutes = (timestamp % 3600) / 60;
2892 seconds = (timestamp % 3600) % 60;
2893 remaining = buf_size - (ptr - formatted_entry);
2894 snprintf(ptr, remaining, "|%"PRIu64"l" "u"":%hhu:%hhu", (uint64_t)hours, minutes, seconds);
2895 if (is_json)
2896 json_object_add_value_string(stats, "Power On Hour", ptr+1);
2897
2898 ptr += 16;
2899
2900 remaining = buf_size - (ptr - formatted_entry);
2901 snprintf(ptr, remaining, "| %"PRIu64"l" "u", le64_to_cpu(entry->power_cycle_count));
2902 if (is_json)
2903 json_object_add_value_int(stats, "Power cycle count",json_object_object_add(stats, "Power cycle count", json_object_new_int
(le32_to_cpu(entry->power_cycle_count)))
2904 le32_to_cpu(entry->power_cycle_count))json_object_object_add(stats, "Power cycle count", json_object_new_int
(le32_to_cpu(entry->power_cycle_count)))
;
2905
2906 ptr += 10;
2907
2908 /* firmware details */
2909 memset(buffer, 0, sizeof(buffer));
2910 memcpy(buffer, entry->previous_fw, sizeof(entry->previous_fw));
2911 remaining = buf_size - (ptr - formatted_entry);
2912 snprintf(ptr, remaining, "| %s", buffer);
2913 if (is_json)
2914 json_object_add_value_string(stats, "Previous firmware", buffer);
2915
2916 ptr += 11;
2917
2918 memset(buffer, 0, sizeof(buffer));
2919 memcpy(buffer, entry->activated_fw, sizeof(entry->activated_fw));
2920 remaining = buf_size - (ptr - formatted_entry);
2921 snprintf(ptr, remaining, "| %s", buffer);
2922 if (is_json)
2923 json_object_add_value_string(stats, "New FW activated", buffer);
2924
2925 ptr += 12;
2926
2927 /* firmware slot and commit action*/
2928 remaining = buf_size - (ptr - formatted_entry);
2929 snprintf(ptr, remaining, "| %d", entry->slot);
2930 if (is_json)
2931 json_object_add_value_int(stats, "Slot number", entry->slot)json_object_object_add(stats, "Slot number", json_object_new_int
(entry->slot))
;
2932
2933 ptr += 9;
2934
2935 remaining = buf_size - (ptr - formatted_entry);
2936 if (entry->commit_action_type <= 3)
2937 snprintf(ptr, remaining, "| %s", ca[entry->commit_action_type]);
2938 else
2939 snprintf(ptr, remaining, "| xxxb");
2940
2941 if (is_json)
2942 json_object_add_value_string(stats, "Commit Action Type", ptr+2);
2943
2944 ptr += 9;
2945
2946 /* result */
2947 remaining = buf_size - (ptr - formatted_entry);
2948 if (entry->result)
2949 snprintf(ptr, remaining, "| Fail #%d", entry->result);
2950 else
2951 snprintf(ptr, remaining, "| pass");
2952
2953 if (is_json) {
2954 json_object_add_value_string(stats, "Result", ptr+2);
2955 return 0;
2956 }
2957
2958 /* replace all null characters with spaces */
2959 ptr = formatted_entry;
2960 while (index < entry_size) {
2961 if (ptr[index] == '\0')
2962 ptr[index] = ' ';
2963 index++;
2964 }
2965 return 0;
2966}
2967
2968static void micron_fw_activation_history_header_print(void)
2969{
2970 /* header to be printed field widths = 10 | 12 | 10 | 11 | 12 | 9 | 9 | 9 */
2971 printf("__________________________________________________________________________________\n");
2972 printf(" | | | | | | |\n");
2973 printf("Firmware | Power On | Power | Previous | New FW | Slot | Commit | Result\n");
2974 printf("Activation| Hour | cycle | firmware | activated | number | Action |\n");
2975 printf("Counter | | count | | | | Type |\n");
2976 printf("__________|___________|_________|__________|___________|________|________|________\n");
2977}
2978
2979static int micron_fw_activation_history(int argc, char **argv, struct command *acmd,
2980 struct plugin *plugin)
2981{
2982 const char *desc = "Retrieve Firmware Activation history of the given drive";
2983 char formatted_output[100];
2984 int count = 0;
2985 unsigned int logC2[C2_log_size4096/sizeof(int)] = { 0 };
2986 enum eDriveModel eModel = UNKNOWN_MODEL;
2987 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
2988 __cleanup_nvme_transport_handle__attribute__((cleanup(cleanup_nvme_transport_handle))) struct libnvme_transport_handle *hdl = NULL((void*)0);
2989 int err;
2990 bool_Bool is_json = false0;
2991 struct json_object *root, *fw_act;
2992 struct json_object *entry;
2993
2994 nvme_print_flags_t format;
2995
2996 OPT_ARGS(opts)struct argconfig_commandline_options opts[] = {
2997 OPT_END(){ ((void*)0) }
2998 };
2999
3000 err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &eModel);
3001 if (err)
3002 return err;
3003
3004 format = nvme_args.output_flags;
3005 is_json = format == JSON;
3006
3007 /* check if product supports fw_history log */
3008 err = -EINVAL22;
3009 if ((eModel != M51CX) && (eModel != M51BY) && (eModel != M51CY)
3010 && (eModel != M6003) && (eModel != M6004)) {
3011 nvme_show_error("Unsupported drive model for vs-fw-activate-history command")nvme_show_message(1, "Unsupported drive model for vs-fw-activate-history command"
)
;
3012 goto out;
3013 }
3014
3015 err = nvme_get_log_simple(hdl, 0xC2, logC2, C2_log_size4096);
3016 if (err) {
3017 nvme_show_err(err, "Failed to retrieve fw activation history log");
3018 goto out;
3019 }
3020
3021 struct micron_fw_activation_history_table *table =
3022 (struct micron_fw_activation_history_table *)logC2;
3023
3024 /* check version and log page */
3025 if (table->log_page != 0xC2 || (table->version != 2 && table->version != 1)) {
3026 nvme_show_error("Unsupported fw activation history page: %x, version: %x",nvme_show_message(1, "Unsupported fw activation history page: %x, version: %x"
, table->log_page, table->version)
3027 table->log_page, table->version)nvme_show_message(1, "Unsupported fw activation history page: %x, version: %x"
, table->log_page, table->version)
;
3028 err = -EINVAL22;
3029 goto out;
3030 }
3031
3032 /* device-supplied entry count stays within the fixed table */
3033 if (le32_to_cpu(table->num_entries) > ARRAY_SIZE(table->entries)(sizeof(table->entries) / sizeof((table->entries)[0]) +
0)
)
3034 table->num_entries = cpu_to_le32(ARRAY_SIZE(table->entries)(sizeof(table->entries) / sizeof((table->entries)[0]) +
0)
);
3035
3036 if (is_json) {
3037 root = json_create_object()json_object_new_object();
3038 fw_act = json_create_object()json_object_new_object();
3039 json_object_add_value_object(root, "vs-fw-activation-history", fw_act)json_object_object_add(root, "vs-fw-activation-history", fw_act
)
;
3040 json_object_add_value_int(fw_act, "Total Entry Num",json_object_object_add(fw_act, "Total Entry Num", json_object_new_int
(le32_to_cpu(table->num_entries)))
3041 le32_to_cpu(table->num_entries))json_object_object_add(fw_act, "Total Entry Num", json_object_new_int
(le32_to_cpu(table->num_entries)))
;
3042 entry = json_create_array()json_object_new_array();
3043 json_object_add_value_array(fw_act, "Entry", entry)json_object_object_add(fw_act, "Entry", entry);
3044 for (count = 0; count < table->num_entries; count++) {
3045 __cleanup_json_object__attribute__((cleanup(json_free_objectp)))
3046 struct json_object *element =
3047 json_create_object()json_object_new_object();
3048
3049 if (!display_fw_activate_entry(count, &table->entries[count],
3050 formatted_output, sizeof(formatted_output),
3051 element)) {
3052 json_array_add_value_object(entry, element)json_object_array_add(entry, element);
3053 element = NULL((void*)0);
3054 }
3055 }
3056 json_print_object(root, NULL)printf("%s", json_object_to_json_string_ext(root, (1 <<
1) | (1 << 4)))
;
3057 printf("\n");
3058 json_free_object(root)json_object_put(root);
3059 } else {
3060 if (!table->num_entries) {
3061 nvme_show_result("No entries were found in fw activation history log")nvme_show_message(0, "No entries were found in fw activation history log"
)
;
3062 goto out;
3063 }
3064 micron_fw_activation_history_header_print();
3065 for (count = 0; count < table->num_entries; count++) {
3066 memset(formatted_output, '\0', 100);
3067 if (!display_fw_activate_entry(count, &table->entries[count],
3068 formatted_output,
3069 sizeof(formatted_output), NULL((void*)0)))
3070 printf("%s\n", formatted_output);
3071 }
3072 }
3073out:
3074 return err;
3075}
3076
3077#define MICRON_FID_LATENCY_MONITOR0xD0 0xD0
3078#define MICRON_LOG_LATENCY_MONITOR0xD1 0xD1
3079
3080static int micron_latency_stats_track(int argc, char **argv, struct command *acmd,
3081 struct plugin *plugin)
3082{
3083 int err = 0;
3084 __u64 result = 0;
3085 const char *desc = "Enable, Disable or Get cmd latency monitoring stats";
3086 const char *option = "enable or disable or status, default is status";
3087 const char *cmdstr =
3088 "commands to monitor for - all|read|write|trim, default is all i.e, enabled for all commands"
3089 ;
3090 const char *thrtime =
3091 "The threshold value to use for latency monitoring in milliseconds, default is 800ms"
3092 ;
3093
3094 int fid = MICRON_FID_LATENCY_MONITOR0xD0;
3095 enum eDriveModel model = UNKNOWN_MODEL;
3096 uint32_t command_mask = 0x7; /* 1:read 2:write 4:trim 7:all */
3097 uint32_t timing_mask = 0x08080800; /* R[31-24]:W[23:16]:T[15:8]:0 */
3098 uint32_t enable = 2;
3099 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
3100 __cleanup_nvme_transport_handle__attribute__((cleanup(cleanup_nvme_transport_handle))) struct libnvme_transport_handle *hdl = NULL((void*)0);
3101 struct {
3102 int option;
3103 int command;
3104 uint32_t threshold;
3105 } opt = {
3106 .option = MICRON_OPTION_STATUS,
3107 .command = MICRON_COMMAND_ALL,
3108 .threshold = 0
3109 };
3110
3111 OPT_ARGS(opts)struct argconfig_commandline_options opts[] = {
3112 OPT_ENUM("option", 'O', &opt.option, option, micron_options){"option", 'O', "NAME", CFG_ENUM, _Generic(&opt.option, int
* : (&opt.option)), ARGCONFIG_ARG_REQUIRED, option, 0, micron_options
, }
,
3113 OPT_ENUM("command", 'c', &opt.command, cmdstr, micron_commands){"command", 'c', "NAME", CFG_ENUM, _Generic(&opt.command,
int * : (&opt.command)), ARGCONFIG_ARG_REQUIRED, cmdstr,
0, micron_commands, }
,
3114 OPT_UINT("threshold", 't', &opt.threshold, thrtime){"threshold", 't', "NUM", CFG_POSITIVE, _Generic(&opt.threshold
, uint32_t * : (&opt.threshold)), ARGCONFIG_ARG_REQUIRED,
thrtime, 0, }
,
3115 OPT_END(){ ((void*)0) }
3116 };
3117
3118
3119 err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &model);
3120 if (err)
3121 return err;
3122
3123 if (opt.option == MICRON_OPTION_ENABLE)
3124 enable = 1;
3125 else if (opt.option == MICRON_OPTION_DISABLE)
3126 enable = 0;
3127
3128 err = nvme_get_features(hdl, 0, fid, 0, 0, 0, NULL((void*)0), 0, &result);
3129 if (err) {
3130 nvme_show_error("Failed to retrieve latency monitoring feature status")nvme_show_message(1, "Failed to retrieve latency monitoring feature status"
)
;
3131 return err;
3132 }
3133
3134 /* If it is to retrieve the status only */
3135 if (enable == 2) {
3136 printf("Latency Tracking Statistics is currently %s",
3137 (result & 0xFFFF0000) ? "enabled" : "disabled");
3138 if ((result & 7) == 7) {
3139 printf(" for All commands\n");
3140 } else if ((result & 7) > 0) {
3141 printf(" for");
3142 if (result & 1)
3143 printf(" Read");
3144 if (result & 2)
3145 printf(" Write");
3146 if (result & 4)
3147 printf(" Trim");
3148 printf(" commands\n");
3149 } else if (!result) {
3150 printf("\n");
3151 }
3152 return err;
3153 }
3154
3155 /* read and validate threshold values if enable option is specified */
3156 if (enable == 1) {
3157 if (opt.threshold > 2550) {
3158 nvme_show_error("The maximum threshold value cannot be more than 2550 ms")nvme_show_message(1, "The maximum threshold value cannot be more than 2550 ms"
)
;
3159 return -1;
3160 } else if (opt.threshold % 10) {
3161 /* timing mask is in terms of 10ms units, so min allowed is 10ms */
3162 nvme_show_error("The threshold value should be multiple of 10 ms")nvme_show_message(1, "The threshold value should be multiple of 10 ms"
)
;
3163 return -1;
3164 }
3165 opt.threshold /= 10;
3166 }
3167
3168 /* read-in command(s) to be monitored */
3169 if (opt.command == MICRON_COMMAND_READ) {
3170 command_mask = 0x1;
3171 timing_mask = (opt.threshold << 24);
3172 } else if (opt.command == MICRON_COMMAND_WRITE) {
3173 command_mask = 0x2;
3174 timing_mask = (opt.threshold << 16);
3175 } else if (opt.command == MICRON_COMMAND_TRIM) {
3176 command_mask = 0x4;
3177 timing_mask = (opt.threshold << 8);
3178 }
3179
3180 err = nvme_set_features(hdl, 0, MICRON_FID_LATENCY_MONITOR0xD0, 1, enable,
3181 command_mask, timing_mask, 0, 0, NULL((void*)0), 0, &result);
3182 if (!err) {
3183 nvme_show_verbose_result("Successfully %sd latency monitoring for %s commands with %dms threshold",nvme_show_verbose_message("Successfully %sd latency monitoring for %s commands with %dms threshold"
, argconfig_value_name(micron_options, opt.option), argconfig_value_name
(micron_commands, opt.command), !opt.threshold ? 800 : opt.threshold
* 10)
3184 argconfig_value_name(micron_options,nvme_show_verbose_message("Successfully %sd latency monitoring for %s commands with %dms threshold"
, argconfig_value_name(micron_options, opt.option), argconfig_value_name
(micron_commands, opt.command), !opt.threshold ? 800 : opt.threshold
* 10)
3185 opt.option),nvme_show_verbose_message("Successfully %sd latency monitoring for %s commands with %dms threshold"
, argconfig_value_name(micron_options, opt.option), argconfig_value_name
(micron_commands, opt.command), !opt.threshold ? 800 : opt.threshold
* 10)
3186 argconfig_value_name(micron_commands,nvme_show_verbose_message("Successfully %sd latency monitoring for %s commands with %dms threshold"
, argconfig_value_name(micron_options, opt.option), argconfig_value_name
(micron_commands, opt.command), !opt.threshold ? 800 : opt.threshold
* 10)
3187 opt.command),nvme_show_verbose_message("Successfully %sd latency monitoring for %s commands with %dms threshold"
, argconfig_value_name(micron_options, opt.option), argconfig_value_name
(micron_commands, opt.command), !opt.threshold ? 800 : opt.threshold
* 10)
3188 !opt.threshold ? 800 : opt.threshold * 10)nvme_show_verbose_message("Successfully %sd latency monitoring for %s commands with %dms threshold"
, argconfig_value_name(micron_options, opt.option), argconfig_value_name
(micron_commands, opt.command), !opt.threshold ? 800 : opt.threshold
* 10)
;
3189 } else {
3190 nvme_show_error("Failed to %s latency monitoring for %s commands with %dms threshold",nvme_show_message(1, "Failed to %s latency monitoring for %s commands with %dms threshold"
, argconfig_value_name(micron_options, opt.option), argconfig_value_name
(micron_commands, opt.command), !opt.threshold ? 800 : opt.threshold
* 10)
3191 argconfig_value_name(micron_options,nvme_show_message(1, "Failed to %s latency monitoring for %s commands with %dms threshold"
, argconfig_value_name(micron_options, opt.option), argconfig_value_name
(micron_commands, opt.command), !opt.threshold ? 800 : opt.threshold
* 10)
3192 opt.option),nvme_show_message(1, "Failed to %s latency monitoring for %s commands with %dms threshold"
, argconfig_value_name(micron_options, opt.option), argconfig_value_name
(micron_commands, opt.command), !opt.threshold ? 800 : opt.threshold
* 10)
3193 argconfig_value_name(micron_commands,nvme_show_message(1, "Failed to %s latency monitoring for %s commands with %dms threshold"
, argconfig_value_name(micron_options, opt.option), argconfig_value_name
(micron_commands, opt.command), !opt.threshold ? 800 : opt.threshold
* 10)
3194 opt.command),nvme_show_message(1, "Failed to %s latency monitoring for %s commands with %dms threshold"
, argconfig_value_name(micron_options, opt.option), argconfig_value_name
(micron_commands, opt.command), !opt.threshold ? 800 : opt.threshold
* 10)
3195 !opt.threshold ? 800 : opt.threshold * 10)nvme_show_message(1, "Failed to %s latency monitoring for %s commands with %dms threshold"
, argconfig_value_name(micron_options, opt.option), argconfig_value_name
(micron_commands, opt.command), !opt.threshold ? 800 : opt.threshold
* 10)
;
3196 }
3197
3198 return err;
3199}
3200
3201
3202static int micron_latency_stats_logs(int argc, char **argv, struct command *acmd,
3203 struct plugin *plugin)
3204{
3205#define LATENCY_LOG_ENTRIES16 16
3206 struct latency_log_entry {
3207 uint64_t timestamp;
3208 uint32_t latency;
3209 uint32_t cmdtag;
3210 union {
3211 struct {
3212 uint32_t opcode:8;
3213 uint32_t fuse:2;
3214 uint32_t rsvd1:4;
3215 uint32_t psdt:2;
3216 uint32_t cid:16;
3217 };
3218 uint32_t dw0;
3219 };
3220 uint32_t nsid;
3221 uint32_t slba_low;
3222 uint32_t slba_high;
3223 union {
3224 struct {
3225 uint32_t nlb:16;
3226 uint32_t rsvd2:9;
3227 uint32_t deac:1;
3228 uint32_t prinfo:4;
3229 uint32_t fua:1;
3230 uint32_t lr:1;
3231 };
3232 uint32_t dw12;
3233 };
3234 uint32_t dsm;
3235 uint32_t rfu[6];
3236 } log[LATENCY_LOG_ENTRIES16];
3237 enum eDriveModel model = UNKNOWN_MODEL;
3238 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
3239 __cleanup_nvme_transport_handle__attribute__((cleanup(cleanup_nvme_transport_handle))) struct libnvme_transport_handle *hdl = NULL((void*)0);
3240 int err = -1;
3241 const char *desc = "Display Latency tracking log information";
3242
3243 OPT_ARGS(opts)struct argconfig_commandline_options opts[] = {
3244 OPT_END(){ ((void*)0) }
3245 };
3246
3247 err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &model);
3248 if (err)
3249 return err;
3250 memset(&log, 0, sizeof(log));
3251 err = nvme_get_log_simple(hdl, 0xD1, &log, sizeof(log));
3252 if (err) {
3253 nvme_show_err(err, "Unable to retrieve the latency stats log");
3254 return err;
3255 }
3256 /* print header and each log entry */
3257 printf("Timestamp, Latency, CmdTag, Opcode, Fuse, Psdt, Cid, Nsid, Slba_L, Slba_H, Nlb, ");
3258 printf("DEAC, PRINFO, FUA, LR\n");
3259 for (int i = 0; i < LATENCY_LOG_ENTRIES16; i++)
3260 printf("%"PRIu64"l" "u"",%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u\n",
3261 log[i].timestamp, log[i].latency, log[i].cmdtag, log[i].opcode,
3262 log[i].fuse, log[i].psdt, log[i].cid, log[i].nsid,
3263 log[i].slba_low, log[i].slba_high, log[i].nlb,
3264 log[i].deac, log[i].prinfo, log[i].fua, log[i].lr);
3265 printf("\n");
3266 return err;
3267}
3268
3269static int micron_latency_stats_info(int argc, char **argv, struct command *acmd,
3270 struct plugin *plugin)
3271{
3272 const char *desc = "display command latency statistics";
3273 const char *cmdstr = "command to display stats - all|read|write|trim, default is all";
3274 int err = 0;
3275 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
3276 __cleanup_nvme_transport_handle__attribute__((cleanup(cleanup_nvme_transport_handle))) struct libnvme_transport_handle *hdl = NULL((void*)0);
3277 enum eDriveModel model = UNKNOWN_MODEL;
3278 #define LATENCY_BUCKET_COUNT32 32
3279 #define LATENCY_BUCKET_RSVD32 32
3280 struct micron_latency_stats {
3281 uint64_t version; /* major << 32 | minior */
3282 uint64_t all_cmds[LATENCY_BUCKET_COUNT32 + LATENCY_BUCKET_RSVD32];
3283 uint64_t read_cmds[LATENCY_BUCKET_COUNT32 + LATENCY_BUCKET_RSVD32];
3284 uint64_t write_cmds[LATENCY_BUCKET_COUNT32 + LATENCY_BUCKET_RSVD32];
3285 uint64_t trim_cmds[LATENCY_BUCKET_COUNT32 + LATENCY_BUCKET_RSVD32];
3286 uint32_t reserved[255]; /* round up to 4K */
3287 } log;
3288
3289 struct latency_thresholds {
3290 uint32_t start;
3291 uint32_t end;
3292 char *unit;
3293 } thresholds[LATENCY_BUCKET_COUNT32] = {
3294 {0, 50, "us"}, {50, 100, "us"}, {100, 150, "us"}, {150, 200, "us"},
3295 {200, 300, "us"}, {300, 400, "us"}, {400, 500, "us"}, {500, 600, "us"},
3296 {600, 700, "us"}, {700, 800, "us"}, {800, 900, "us"}, {900, 1000, "us"},
3297 {1, 5, "ms"}, {5, 10, "ms"}, {10, 20, "ms"}, {20, 50, "ms"}, {50, 100, "ms"},
3298 {100, 200, "ms"}, {200, 300, "ms"}, {300, 400, "ms"}, {400, 500, "ms"},
3299 {500, 600, "ms"}, {600, 700, "ms"}, {700, 800, "ms"}, {800, 900, "ms"},
3300 {900, 1000, "ms"}, {1, 2, "s"}, {2, 3, "s"}, {3, 4, "s"}, {4, 5, "s"},
3301 {5, 8, "s"},
3302 {8, INT_MAX2147483647, "s"},
3303 };
3304
3305 struct {
3306 int command;
3307 } opt = {
3308 .command = MICRON_COMMAND_ALL
3309 };
3310
3311 uint64_t *cmd_stats = &log.all_cmds[0];
3312 char *cmd_str = "All";
3313
3314 OPT_ARGS(opts)struct argconfig_commandline_options opts[] = {
3315 OPT_ENUM("command", 'c', &opt.command, cmdstr, micron_commands){"command", 'c', "NAME", CFG_ENUM, _Generic(&opt.command,
int * : (&opt.command)), ARGCONFIG_ARG_REQUIRED, cmdstr,
0, micron_commands, }
,
3316 OPT_END(){ ((void*)0) }
3317 };
3318
3319 err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &model);
3320 if (err)
3321 return err;
3322 if (opt.command == MICRON_COMMAND_READ) {
3323 cmd_stats = &log.read_cmds[0];
3324 cmd_str = "Read";
3325 } else if (opt.command == MICRON_COMMAND_WRITE) {
3326 cmd_stats = &log.write_cmds[0];
3327 cmd_str = "Write";
3328 } else if (opt.command == MICRON_COMMAND_TRIM) {
3329 cmd_stats = &log.trim_cmds[0];
3330 cmd_str = "Trim";
3331 }
3332
3333 memset(&log, 0, sizeof(log));
3334 err = nvme_get_log_simple(hdl, 0xD0, &log, sizeof(log));
3335 if (err) {
3336 nvme_show_err(err, "Unable to retrieve latency stats log for the drive");
3337 return err;
3338 }
3339 printf("Micron IO %s Command Latency Statistics\n"
3340 "Major Revision : %d\nMinor Revision : %d\n",
3341 cmd_str, (int)(log.version >> 32), (int)(log.version & 0xFFFFFFFF));
3342 printf("=============================================\n");
3343 printf("Bucket Start End Command Count\n");
3344 printf("=============================================\n");
3345
3346 for (int b = 0; b < LATENCY_BUCKET_COUNT32; b++) {
3347 int bucket = b + 1;
3348 char start[32] = { 0 };
3349 char end[32] = { 0 };
3350
3351 snprintf(start, sizeof(start), "%u%s", thresholds[b].start,
3352 thresholds[b].unit);
3353 if (thresholds[b].end == INT_MAX2147483647)
3354 snprintf(end, sizeof(end), "INF");
3355 else
3356 snprintf(end, sizeof(end), "%u%s", thresholds[b].end,
3357 thresholds[b].unit);
3358 printf("%2d %8s %8s %8"PRIu64"l" "u""\n", bucket, start, end, cmd_stats[b]);
3359 }
3360 return err;
3361}
3362
3363static int micron_ocp_smart_health_logs(int argc, char **argv, struct command *acmd,
3364 struct plugin *plugin)
3365{
3366 const char *desc = "Retrieve Smart or Extended Smart Health log for the given device ";
3367 unsigned int logC0[C0_log_size512/sizeof(int)] = { 0 };
3368 unsigned int logFB[FB_log_size512/sizeof(int)] = { 0 };
3369 struct nvme_id_ctrl ctrl;
3370 enum eDriveModel eModel = UNKNOWN_MODEL;
3371 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
3372 __cleanup_nvme_transport_handle__attribute__((cleanup(cleanup_nvme_transport_handle))) struct libnvme_transport_handle *hdl = NULL((void*)0);
3373 bool_Bool is_json = false0;
3374 nsze_from_oacs = false0;
3375 nvme_print_flags_t format;
3376 int err = 0;
3377
3378 OPT_ARGS(opts)struct argconfig_commandline_options opts[] = {
3379 OPT_END(){ ((void*)0) }
3380 };
3381
3382 err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &eModel);
3383 if (err)
3384 return err;
3385
3386 format = nvme_args.output_flags;
3387 is_json = format == JSON;
3388
3389 /* For M5410 and M5407, this option prints 0xFB log page */
3390 if (eModel == M5410 || eModel == M5407) {
3391 __u8 spec = (eModel == M5410) ? 0 : 1;
3392 __u8 nsze;
3393 struct libnvme_passthru_cmd cmd;
3394
3395 nvme_init_identify_ctrl(&cmd, &ctrl);
3396 err = libnvme_exec_admin_passthru(hdl, &cmd);
3397 if (!err)
3398 err = nvme_get_log_simple(hdl, 0xFB, logFB, FB_log_size512);
3399 if (err) {
3400 nvme_show_err(err, "Unable to retrieve smart log 0xFB for the drive");
3401 goto out;
3402 }
3403
3404 nsze = (ctrl.vs[987] == 0x12);
3405 if (!nsze && nsze_from_oacs)
3406 nsze = ((ctrl.oacs >> 3) & 0x1);
3407 print_nand_stats_fb((__u8 *)logFB, NULL((void*)0), nsze, is_json, spec);
3408 goto out;
3409 }
3410
3411 /* check for models that support 0xC0 log */
3412 if ((eModel != M51CX) && (eModel != M51BY) && (eModel != M51CY)
3413 && (eModel != M6003) && (eModel != M6004)) {
3414 nvme_show_error("Unsupported drive model for vs-smart-add-log command")nvme_show_message(1, "Unsupported drive model for vs-smart-add-log command"
)
;
3415 err = -1;
3416 goto out;
3417 }
3418
3419 err = nvme_get_log_simple(hdl, 0xC0, logC0, C0_log_size512);
3420 if (!err)
3421 print_smart_cloud_health_log((__u8 *)logC0, is_json, eModel);
3422 else
3423 nvme_show_err(err, "Unable to retrieve extended smart log 0xC0 for the drive");
3424out:
3425 return err;
3426}
3427
3428static int micron_clr_fw_activation_history(int argc, char **argv,
3429 struct command *command, struct plugin *plugin)
3430{
3431 const char *desc = "Clear FW activation history";
3432 __u64 result = 0;
3433 __u8 fid = MICRON_FEATURE_CLEAR_FW_ACTIVATION_HISTORY0xC1;
3434 enum eDriveModel model = UNKNOWN_MODEL;
3435 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
3436 __cleanup_nvme_transport_handle__attribute__((cleanup(cleanup_nvme_transport_handle))) struct libnvme_transport_handle *hdl = NULL((void*)0);
3437
3438 OPT_ARGS(opts)struct argconfig_commandline_options opts[] = {
3439 OPT_END(){ ((void*)0) }
3440 };
3441 int err = 0;
3442
3443 err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &model);
3444 if (err)
3445 return err;
3446
3447 if ((model != M51CX) && (model != M51BY) && (model != M51CY)
3448 && (model != M6003) && (model != M6004)) {
3449 nvme_show_error("This option is not supported for specified drive")nvme_show_message(1, "This option is not supported for specified drive"
)
;
3450 return -ENOTSUP95;
3451 }
3452
3453 err = nvme_set_features_simple(hdl, 0, fid, false0, 1U << 31, &result);
3454 if (!err)
3455 err = (int)result;
3456 else
3457 nvme_show_err(err, "Failed to clear fw activation history");
3458
3459 return err;
3460}
3461
3462static int micron_telemetry_cntrl_option(int argc, char **argv,
3463 struct command *command, struct plugin *plugin)
3464{
3465 int err = 0;
3466 __u64 result = 0;
3467 const char *desc = "Enable or Disable Controller telemetry log generation";
3468 const char *option = "enable or disable or status";
3469 const char *select =
3470 "select/save values: enable/disable options1 - save (persistent), 0 - non-persistent and for status options: 0 - current, 1 - default, 2-saved"
3471 ;
3472
3473 int fid = MICRON_FEATURE_TELEMETRY_CONTROL_OPTION0xCF;
3474 enum eDriveModel model = UNKNOWN_MODEL;
3475 struct nvme_id_ctrl ctrl = { 0 };
3476 struct libnvme_passthru_cmd cmd;
3477 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
3478 __cleanup_nvme_transport_handle__attribute__((cleanup(cleanup_nvme_transport_handle))) struct libnvme_transport_handle *hdl = NULL((void*)0);
3479
3480 struct {
3481 int option;
3482 __u32 select;
3483 } opt = {
3484 .option = MICRON_OPTION_DISABLE,
3485 .select = 0,
3486 };
3487
3488 OPT_ARGS(opts)struct argconfig_commandline_options opts[] = {
3489 OPT_ENUM("option", 'O', &opt.option, option, micron_options){"option", 'O', "NAME", CFG_ENUM, _Generic(&opt.option, int
* : (&opt.option)), ARGCONFIG_ARG_REQUIRED, option, 0, micron_options
, }
,
3490 OPT_UINT("select", 's', &opt.select, select){"select", 's', "NUM", CFG_POSITIVE, _Generic(&opt.select
, uint32_t * : (&opt.select)), ARGCONFIG_ARG_REQUIRED, select
, 0, }
,
3491 OPT_END(){ ((void*)0) }
3492 };
3493
3494 err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &model);
3495 if (err)
3496 return err;
3497
3498 nvme_init_identify_ctrl(&cmd, &ctrl);
3499 err = libnvme_exec_admin_passthru(hdl, &cmd);
3500 if ((ctrl.lpa & 0x8) != 0x8) {
3501 printf("drive doesn't support host/controller generated telemetry logs\n");
3502 return err;
3503 }
3504
3505 if (opt.option == MICRON_OPTION_ENABLE) {
3506 err = nvme_set_features(hdl, 1, fid, (opt.select & 0x1), 1, 0, 0, 0, 0,
3507 NULL((void*)0), 0, &result);
3508 if (!err)
3509 nvme_show_verbose_result("successfully set controller telemetry option")nvme_show_verbose_message("successfully set controller telemetry option"
)
;
3510 else
3511 nvme_show_error("Failed to set controller telemetry option")nvme_show_message(1, "Failed to set controller telemetry option"
)
;
3512 } else if (opt.option == MICRON_OPTION_DISABLE) {
3513 err = nvme_set_features(hdl, 1, fid, (opt.select & 0x1), 0, 0, 0, 0, 0,
3514 NULL((void*)0), 0, &result);
3515 if (!err)
3516 nvme_show_verbose_result("successfully disabled controller telemetry option")nvme_show_verbose_message("successfully disabled controller telemetry option"
)
;
3517 else
3518 nvme_show_error("Failed to disable controller telemetry option")nvme_show_message(1, "Failed to disable controller telemetry option"
)
;
3519 } else {
3520 err = nvme_get_features(hdl, 1, fid, opt.select & 0x3, 0, 0, NULL((void*)0), 0,
3521 &result);
3522 if (!err)
3523 printf("Controller telemetry option : %s\n",
3524 (result) ? "enabled" : "disabled");
3525 else
3526 nvme_show_error("Failed to retrieve controller telemetry option")nvme_show_message(1, "Failed to retrieve controller telemetry option"
)
;
3527 }
3528
3529 return err;
3530}
3531
3532/* M51XX models log page header */
3533struct micron_common_log_header {
3534 uint8_t id;
3535 uint8_t version;
3536 uint16_t pn;
3537 uint32_t log_size;
3538 uint32_t max_size;
3539 uint32_t write_pointer;
3540 uint32_t next_pointer;
3541 uint32_t overwritten_bytes;
3542 uint8_t flags;
3543 uint8_t reserved[7];
3544};
3545
3546/* helper function to retrieve logs with specific offset and max chunk size */
3547int nvme_get_log_lpo(struct libnvme_transport_handle *hdl, __u8 log_id, __u32 lpo, __u32 chunk,
3548 __u32 data_len, void *data)
3549{
3550 __u32 offset = lpo, xfer_len = data_len;
3551 struct libnvme_passthru_cmd cmd;
3552 void *ptr = data;
3553 int ret = 0;
3554
3555 /* divide data into multiple chunks */
3556 do {
3557 xfer_len = data_len - offset;
3558 if (xfer_len > chunk)
3559 xfer_len = chunk;
3560
3561 nvme_init_get_log(&cmd, NVME_NSID_ALL, log_id, NVME_CSI_NVM,
3562 ptr, xfer_len);
3563 nvme_init_get_log_lpo(&cmd, lpo);
3564 ret = libnvme_get_log(hdl, &cmd, false0, xfer_len);
3565 if (ret)
3566 return ret;
3567 offset += xfer_len;
3568 ptr += xfer_len;
3569 } while (offset < data_len);
3570 return ret;
3571}
3572
3573/* retrieves logs with common log format */
3574static int get_common_log(struct libnvme_transport_handle *hdl, uint8_t id, uint8_t **buf, int *size)
3575{
3576 struct micron_common_log_header hdr = { 0 };
3577 int log_size = sizeof(hdr), first = 0, second = 0;
3578 uint8_t *buffer = NULL((void*)0);
3579 int ret = -1;
3580 int chunk = 0x4000; /* max chunk size to be used for these logs */
3581
3582 ret = nvme_get_log_simple(hdl, id, &hdr, sizeof(hdr));
3583 if (ret) {
3584 nvme_show_error("pull hdr failed for %u with error: 0x%x", id, ret)nvme_show_message(1, "pull hdr failed for %u with error: 0x%x"
, id, ret)
;
3585 return ret;
3586 }
3587
3588 if (hdr.id != id || !hdr.log_size || !hdr.max_size ||
3589 hdr.write_pointer < sizeof(hdr)) {
3590 nvme_show_error(nvme_show_message(1, "invalid log data for LOG: 0x%X, id: 0x%X, size: %u, max: %u, wp: %u, flags: %u, np: %u\n"
, id, hdr.id, hdr.log_size, hdr.max_size, hdr.write_pointer, hdr
.flags, hdr.next_pointer)
3591 "invalid log data for LOG: 0x%X, id: 0x%X, size: %u, max: %u, wp: %u, flags: %u, np: %u\n"nvme_show_message(1, "invalid log data for LOG: 0x%X, id: 0x%X, size: %u, max: %u, wp: %u, flags: %u, np: %u\n"
, id, hdr.id, hdr.log_size, hdr.max_size, hdr.write_pointer, hdr
.flags, hdr.next_pointer)
3592 , id, hdr.id, hdr.log_size, hdr.max_size, hdr.write_pointer, hdr.flags,nvme_show_message(1, "invalid log data for LOG: 0x%X, id: 0x%X, size: %u, max: %u, wp: %u, flags: %u, np: %u\n"
, id, hdr.id, hdr.log_size, hdr.max_size, hdr.write_pointer, hdr
.flags, hdr.next_pointer)
3593 hdr.next_pointer)nvme_show_message(1, "invalid log data for LOG: 0x%X, id: 0x%X, size: %u, max: %u, wp: %u, flags: %u, np: %u\n"
, id, hdr.id, hdr.log_size, hdr.max_size, hdr.write_pointer, hdr
.flags, hdr.next_pointer)
;
3594 return 1;
3595 }
3596
3597 /*
3598 * we may have just 32-bytes for some models; write to wfile if log hasn't
3599 * yet reached its max size
3600 */
3601 if (hdr.log_size == sizeof(hdr)) {
3602 buffer = (uint8_t *)libnvme_alloc(sizeof(hdr));
3603 if (!buffer) {
3604 nvme_show_error("malloc of %zu bytes failed for log: 0x%X",nvme_show_message(1, "malloc of %zu bytes failed for log: 0x%X"
, sizeof(hdr), id)
3605 sizeof(hdr), id)nvme_show_message(1, "malloc of %zu bytes failed for log: 0x%X"
, sizeof(hdr), id)
;
3606 return -ENOMEM12;
3607 }
3608 memcpy(buffer, (uint8_t *)&hdr, sizeof(hdr));
3609 } else if (hdr.log_size < hdr.max_size) {
3610 /* log_size includes the header, so the payload is the remainder */
3611 uint32_t payload = hdr.log_size - sizeof(hdr);
3612
3613 buffer = (uint8_t *)libnvme_alloc(hdr.log_size);
3614 if (!buffer) {
3615 nvme_show_error("malloc of %u bytes failed for log: 0x%X",nvme_show_message(1, "malloc of %u bytes failed for log: 0x%X"
, hdr.log_size, id)
3616 hdr.log_size, id)nvme_show_message(1, "malloc of %u bytes failed for log: 0x%X"
, hdr.log_size, id)
;
3617 return -ENOMEM12;
3618 }
3619 memcpy(buffer, &hdr, sizeof(hdr));
3620 ret = nvme_get_log_lpo(hdl, id, sizeof(hdr), chunk, payload,
3621 buffer + sizeof(hdr));
3622 if (!ret)
3623 log_size += payload;
3624 } else if (hdr.log_size >= hdr.max_size) {
3625 /*
3626 * reached maximum, to maintain, sequence we need to depend on write
3627 * pointer to detect wrap-overs. FW doesn't yet implement the condition
3628 * hdr.log_size > hdr.max_size; also ignore over-written log data; we
3629 * also ignore collisions for now
3630 */
3631 buffer = (uint8_t *)libnvme_alloc(hdr.max_size + sizeof(hdr));
3632 if (!buffer) {
3633 nvme_show_error("malloc of %zu bytes failed for log: 0x%X",nvme_show_message(1, "malloc of %zu bytes failed for log: 0x%X"
, hdr.max_size + sizeof(hdr), id)
3634 hdr.max_size + sizeof(hdr), id)nvme_show_message(1, "malloc of %zu bytes failed for log: 0x%X"
, hdr.max_size + sizeof(hdr), id)
;
3635 return -ENOMEM12;
3636 }
3637 memcpy(buffer, &hdr, sizeof(hdr));
3638
3639 first = hdr.max_size - hdr.write_pointer;
3640 second = hdr.write_pointer - sizeof(hdr);
3641
3642 if (first) {
3643 ret = nvme_get_log_lpo(hdl, id, hdr.write_pointer, chunk, first,
3644 buffer + sizeof(hdr));
3645 if (ret) {
3646 libnvme_free(buffer);
3647 nvme_show_error("failed to get log: 0x%X", id)nvme_show_message(1, "failed to get log: 0x%X", id);
3648 return ret;
3649 }
3650 log_size += first;
3651 }
3652 if (second) {
3653 ret = nvme_get_log_lpo(hdl, id, sizeof(hdr), chunk, second,
3654 buffer + sizeof(hdr) + first);
3655 if (ret) {
3656 nvme_show_error("failed to get log: 0x%X", id)nvme_show_message(1, "failed to get log: 0x%X", id);
3657 libnvme_free(buffer);
3658 return ret;
3659 }
3660 log_size += second;
3661 }
3662 }
3663 *buf = buffer;
3664 *size = log_size;
3665 return ret;
3666}
3667
3668static int GetOcpEnhancedTelemetryLogs(struct libnvme_transport_handle *hdl, const char *dir)
3669{
3670 int err = 0;
3671 unsigned int uiBufferSize = 512;
3672 unsigned char pBuffer[512] = { 0 };
3673 __u64 result = 0;
3674
3675 pBuffer[1] = 1;
3676
3677 /* Enable ETDAS so Data Area 4 is included in the telemetry logs */
3678 err = nvme_set_features(hdl, NVME_NSID_ALL,
3679 MICRON_FEATURE_OCP_ENHANCED_TELEMETRY0x16, 1, 0, 0, 0,
3680 0, 0, pBuffer, uiBufferSize, &result);
3681
3682 if (err)
3683 nvme_show_error("Failed to set ETDAS, Data Area 4 won't be available")nvme_show_message(1, "Failed to set ETDAS, Data Area 4 won't be available"
)
;
3684
3685 return GetTelemetryData(hdl, dir, !err);
3686}
3687
3688static int micron_internal_logs(int argc, char **argv, struct command *acmd,
3689 struct plugin *plugin)
3690{
3691 int err = -EINVAL22;
3692 int ctrlIdx, telemetry_option = 0;
3693 int ns_err;
3694 char strOSDirName[1024];
3695 char strCtrlDirName[1024];
3696 char strMainDirName[256];
3697 unsigned int *puiIDDBuf;
3698 unsigned int uiMask;
3699 struct nvme_id_ctrl ctrl;
3700 struct libnvme_passthru_cmd cmd;
3701 char safe_sn[sizeof(ctrl.sn) + 1] = { 0 };
3702 char msg[256] = { 0 };
3703 int c_logs_index = 8; /* should be current size of aVendorLogs */
3704 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
3705 __cleanup_nvme_transport_handle__attribute__((cleanup(cleanup_nvme_transport_handle))) struct libnvme_transport_handle *hdl = NULL((void*)0);
3706 struct {
3707 unsigned char ucLogPage;
3708 const char *strFileName;
3709 int nLogSize;
3710 int nMaxSize;
3711 } aVendorLogs[32] = {
3712 { 0x03, LOG_FILE_FW_SLOT"firmware_slot_info_log.bin", 512, 0 },
3713 { 0xC1, LOG_FILE_VS_LOG(C1)"nvmelog_" "C1" ".bin", 0, 0 },
3714 { 0xC2, LOG_FILE_VS_LOG(C2)"nvmelog_" "C2" ".bin", 0, 0 },
3715 { 0xC4, LOG_FILE_VS_LOG(C4)"nvmelog_" "C4" ".bin", 0, 0 },
3716 { 0xC5, LOG_FILE_VS_LOG(C5)"nvmelog_" "C5" ".bin", C5_log_size(((452 + 16 * 1024) / 4) * 4096), 0 },
3717 { 0xD0, LOG_FILE_VS_LOG(D0)"nvmelog_" "D0" ".bin", D0_log_size512, 0 },
3718 { 0xE6, LOG_FILE_VS_LOG(E6)"nvmelog_" "E6" ".bin", 0, 0 },
3719 { 0xE7, LOG_FILE_VS_LOG(E7)"nvmelog_" "E7" ".bin", 0, 0 }
3720 },
3721 aM51XXLogs[] = {
3722 /* this should be collected first for M51AX */
3723 { 0xFB, LOG_FILE_VS_LOG(FB)"nvmelog_" "FB" ".bin", 4096, 0 },
3724 { 0xD0, LOG_FILE_VS_LOG(D0)"nvmelog_" "D0" ".bin", 512, 0 },
3725 { 0x03, LOG_FILE_FW_SLOT"firmware_slot_info_log.bin", 512, 0},
3726 { 0xF7, LOG_FILE_VS_LOG(F7)"nvmelog_" "F7" ".bin", 4096, 512 * 1024 },
3727 { 0xF8, LOG_FILE_VS_LOG(F8)"nvmelog_" "F8" ".bin", 4096, 512 * 1024 },
3728 { 0xF9, LOG_FILE_VS_LOG(F9)"nvmelog_" "F9" ".bin", 4096, 200 * 1024 * 1024 },
3729 { 0xFC, LOG_FILE_VS_LOG(FC)"nvmelog_" "FC" ".bin", 4096, 200 * 1024 * 1024 },
3730 { 0xFD, LOG_FILE_VS_LOG(FD)"nvmelog_" "FD" ".bin", 4096, 80 * 1024 * 1024 }
3731 },
3732 aM51AXLogs[] = {
3733 { 0xCA, LOG_FILE_VS_LOG(CA)"nvmelog_" "CA" ".bin", 512, 0 },
3734 { 0xFA, LOG_FILE_VS_LOG(FA)"nvmelog_" "FA" ".bin", 4096, 15232 },
3735 { 0xF6, LOG_FILE_VS_LOG(F6)"nvmelog_" "F6" ".bin", 4096, 512 * 1024 },
3736 { 0xFE, LOG_FILE_VS_LOG(FE)"nvmelog_" "FE" ".bin", 4096, 512 * 1024 },
3737 { 0xFF, LOG_FILE_VS_LOG(FF)"nvmelog_" "FF" ".bin", 4096, 162 * 1024 },
3738 { 0x04, LOG_FILE_CHANGED_NS"changed_namespace_log.bin", 4096, 0 },
3739 { 0x05, LOG_FILE_CMD_EFFECTS"command_effects_log.bin", 4096, 0 },
3740 { 0x06, LOG_FILE_SELF_TEST"drive_self_test.bin", 4096, 0 }
3741 },
3742 aM51BXLogs[] = {
3743 { 0xFA, LOG_FILE_VS_LOG(FA)"nvmelog_" "FA" ".bin", 4096, 16376 },
3744 { 0xFE, LOG_FILE_VS_LOG(FE)"nvmelog_" "FE" ".bin", 4096, 256 * 1024 },
3745 { 0xFF, LOG_FILE_VS_LOG(FF)"nvmelog_" "FF" ".bin", 4096, 64 * 1024 },
3746 { 0xCA, LOG_FILE_VS_LOG(CA)"nvmelog_" "CA" ".bin", 512, 1024 }
3747 },
3748 aM51CXLogs[] = {
3749 { 0xE1, LOG_FILE_VS_LOG(E1)"nvmelog_" "E1" ".bin", 0, 0 },
3750 { 0xE2, LOG_FILE_VS_LOG(E2)"nvmelog_" "E2" ".bin", 0, 0 },
3751 { 0xE3, LOG_FILE_VS_LOG(E3)"nvmelog_" "E3" ".bin", 0, 0 },
3752 { 0xE4, LOG_FILE_VS_LOG(E4)"nvmelog_" "E4" ".bin", 0, 0 },
3753 { 0xE5, LOG_FILE_VS_LOG(E5)"nvmelog_" "E5" ".bin", 0, 0 },
3754 { 0xE8, LOG_FILE_VS_LOG(E8)"nvmelog_" "E8" ".bin", 0, 0 },
3755 { 0xE9, LOG_FILE_VS_LOG(E9)"nvmelog_" "E9" ".bin", 0, 0 },
3756 { 0xEA, LOG_FILE_VS_LOG(EA)"nvmelog_" "EA" ".bin", 0, 0 }
3757 };
3758
3759 enum eDriveModel eModel = UNKNOWN_MODEL;
3760
3761 const char *desc = "This retrieves the micron debug log package";
3762 const char *package = "Log output data file name (required)";
3763 const char *type = "telemetry log type - host or controller";
3764 const char *data_area = "telemetry log data area 1, 2, 3 or 4";
3765 unsigned char *dataBuffer = NULL((void*)0);
3766 int bSize = 0;
3767 int maxSize = 0;
3768 struct MICRON_WORKLOAD_LOG_HDR stWllHdr = { 0 };
3769
3770 struct argconfig_opt_val types[] = {
3771 VAL_ENUM("host", 0x07){"host", CFG_ENUM, .val.int_val = 0x07},
3772 VAL_ENUM("controller", 0x08){"controller", CFG_ENUM, .val.int_val = 0x08},
3773 VAL_END(){ ((void*)0) }
3774 };
3775
3776 struct config {
3777 int type;
3778 char *package;
3779 __u32 data_area;
3780 int log;
3781 };
3782
3783 struct config cfg = {
3784 .type = 0x07,
3785 .package = "",
3786 .data_area = 0,
3787 .log = 0x07,
3788 };
3789
3790 OPT_ARGS(opts)struct argconfig_commandline_options opts[] = {
3791 OPT_ENUM("type", 't', &cfg.type, type, types){"type", 't', "NAME", CFG_ENUM, _Generic(&cfg.type, int *
: (&cfg.type)), ARGCONFIG_ARG_REQUIRED, type, 0, types, }
,
3792 OPT_FILE("package", 'p', &cfg.package, package){"package", 'p', "FILE", CFG_FILE, _Generic(&cfg.package,
char ** : (&cfg.package), const char ** : (&cfg.package
)), ARGCONFIG_ARG_REQUIRED, package, 0, }
,
3793 OPT_UINT("data_area", 'd', &cfg.data_area, data_area){"data_area", 'd', "NUM", CFG_POSITIVE, _Generic(&cfg.data_area
, uint32_t * : (&cfg.data_area)), ARGCONFIG_ARG_REQUIRED,
data_area, 0, }
,
3794 OPT_END(){ ((void*)0) }
3795 };
3796
3797 err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &eModel);
3798 if (err)
3799 return err;
3800
3801 err = -EINVAL22;
3802 /* if telemetry type is specified, check for data area */
3803 if (argconfig_given(opts, &cfg.type)) {
3804 cfg.log = cfg.type;
3805
3806 if (!cfg.data_area || cfg.data_area > 4) {
3807 nvme_show_error("data area must be selected using -d option ie --d=1,2,3,4")nvme_show_message(1, "data area must be selected using -d option ie --d=1,2,3,4"
)
;
3808 goto out;
3809 }
3810 telemetry_option = 1;
3811 } else if (argconfig_given(opts, &cfg.data_area)) {
3812 nvme_show_error(nvme_show_message(1, "data area option is valid only for telemetry option (i.e --type=host|controller)"
)
3813 "data area option is valid only for telemetry option (i.e --type=host|controller)")nvme_show_message(1, "data area option is valid only for telemetry option (i.e --type=host|controller)"
)
;
3814 goto out;
3815 }
3816
3817 if (!strlen(cfg.package)) {
3818 if (telemetry_option)
3819 nvme_show_error("Log data file must be specified. ie -p=logfile.bin")nvme_show_message(1, "Log data file must be specified. ie -p=logfile.bin"
)
;
3820 else
3821 nvme_show_error(nvme_show_message(1, "Log data file must be specified. ie -p=logfile.zip or -p=logfile.tgz|logfile.tar.gz"
)
3822 "Log data file must be specified. ie -p=logfile.zip or -p=logfile.tgz|logfile.tar.gz")nvme_show_message(1, "Log data file must be specified. ie -p=logfile.zip or -p=logfile.tgz|logfile.tar.gz"
)
;
3823 goto out;
3824 }
3825
3826 if (!is_safe_path(cfg.package)) {
3827 nvme_show_error(nvme_show_message(1, "Invalid package path: contains unsafe characters\n"
)
3828 "Invalid package path: contains unsafe characters\n")nvme_show_message(1, "Invalid package path: contains unsafe characters\n"
)
;
3829 goto out;
3830 }
3831
3832 /* pull log details based on the model name */
3833 if (eModel == UNKNOWN_MODEL) {
3834 nvme_show_error("Unsupported drive model for vs-internal-log collection")nvme_show_message(1, "Unsupported drive model for vs-internal-log collection"
)
;
3835 err = -ENOTSUP95;
3836 goto out;
3837 }
3838
3839 if (sscanf(libnvme_transport_handle_get_name(hdl), "nvme%d", &ctrlIdx) != 1)
3840 ctrlIdx = 0;
3841
3842 nvme_init_identify_ctrl(&cmd, &ctrl);
3843 err = libnvme_exec_admin_passthru(hdl, &cmd);
3844 if (err)
3845 goto out;
3846
3847 err = -EINVAL22;
3848 if (telemetry_option) {
3849 if ((ctrl.lpa & 0x8) != 0x8) {
3850 nvme_show_error("telemetry option is not supported for specified drive")nvme_show_message(1, "telemetry option is not supported for specified drive"
)
;
3851 goto out;
3852 }
3853 uint32_t logSize = 0; __u8 *buffer = NULL((void*)0);
3854
3855 err = micron_telemetry_log(hdl, cfg.log, &buffer, &logSize,
3856 cfg.data_area);
3857 if (!err && logSize > 0 && buffer) {
3858 snprintf(msg, sizeof(msg), "telemetry log: 0x%X", cfg.log);
3859 WriteData(buffer, logSize, NULL((void*)0), cfg.package, msg);
3860 libnvme_free(buffer);
3861 }
3862 goto out;
3863 }
3864
3865 printf("Preparing log package. This will take a few seconds...\n");
3866
3867 memcpy(safe_sn, ctrl.sn, sizeof(ctrl.sn));
3868 shr_sanitize_name(shr_rtrim(safe_sn));
3869 err = SetupDebugDataDirectories(safe_sn, cfg.package,
3870 strMainDirName, sizeof(strMainDirName),
3871 strOSDirName, sizeof(strOSDirName),
3872 strCtrlDirName, sizeof(strCtrlDirName));
3873 if (err) {
3874 nvme_show_error("Failed to create debug data directories")nvme_show_message(1, "Failed to create debug data directories"
)
;
3875 goto out;
3876 }
3877
3878 /*
3879 * Set up the cmd status CSV before any collection so each helper can
3880 * append its own row.
3881 */
3882 SetupCmdStatusLog(strCtrlDirName, safe_sn);
3883
3884 GetLogPullMetadata(strOSDirName, &ctrl, plugin->version);
3885 GetTimestampInfo(strOSDirName);
3886 GetCtrlIDDInfo(strCtrlDirName, &ctrl);
3887 GetOSConfig(strOSDirName);
3888 GetDriveInfo(strOSDirName, ctrlIdx, &ctrl);
3889
3890 /*
3891 * All namespaces share a single command info entry.
3892 * Report success if any namespace was collected.
3893 */
3894 ns_err = -1;
3895 for (int i = 1; i <= ctrl.nn; i++) {
3896 if (!GetNSIDDInfo(hdl, strCtrlDirName, i))
3897 ns_err = 0;
3898 }
3899 LogCmdStatus(LOG_FILE_ID_NS"identify_namespace_%d_data.bin", ns_err);
3900
3901 GetSmartlogData(hdl, strCtrlDirName);
3902 GetErrorlogData(hdl, ctrl.elpe, strCtrlDirName);
3903 GetGenericLogs(hdl, strCtrlDirName);
3904 /* pull if telemetry log data is supported */
3905 if ((ctrl.lpa & 0x8) == 0x8) {
3906 if (eModel == M51BY)
3907 GetOcpEnhancedTelemetryLogs(hdl, strCtrlDirName);
3908 else
3909 GetTelemetryData(hdl, strCtrlDirName, ctrl.lpa & 0x40);
3910 }
3911 GetFeatureSettings(hdl, strCtrlDirName);
3912
3913 if (eModel != M5410 && eModel != M5407) {
3914 memcpy(&aVendorLogs[c_logs_index], aM51XXLogs, sizeof(aM51XXLogs));
3915 c_logs_index += ARRAY_SIZE(aM51XXLogs)(sizeof(aM51XXLogs) / sizeof((aM51XXLogs)[0]) + 0);
3916 if (eModel == M51AX)
3917 memcpy((char *)&aVendorLogs[c_logs_index], aM51AXLogs, sizeof(aM51AXLogs));
3918 else if (eModel == M51BX)
3919 memcpy((char *)&aVendorLogs[c_logs_index], aM51BXLogs, sizeof(aM51BXLogs));
3920 else if (eModel == M51CX || eModel == M51BY || eModel == M51CY)
3921 memcpy((char *)&aVendorLogs[c_logs_index], aM51CXLogs, sizeof(aM51CXLogs));
3922 }
3923
3924 for (int i = 0; i < (int)(ARRAY_SIZE(aVendorLogs)(sizeof(aVendorLogs) / sizeof((aVendorLogs)[0]) + 0)) && aVendorLogs[i].ucLogPage; i++) {
3925 bool_Bool wrote = false0;
3926
3927 err = -1;
3928 switch (aVendorLogs[i].ucLogPage) {
3929 case 0xE1:
3930 case 0xE5:
3931 continue;
3932 case 0xE9:
3933 if (eModel != M51CX && eModel != M51BY)
3934 continue;
3935
3936 err = NVMEGetLogPage(hdl, aVendorLogs[i].ucLogPage,
3937 (unsigned char *)&stWllHdr,
3938 sizeof(struct MICRON_WORKLOAD_LOG_HDR), 0);
3939 if (err == 0) {
3940 bSize = stWllHdr.uiLength;
3941 if (bSize < (int)sizeof(struct MICRON_WORKLOAD_LOG_HDR)) {
3942 nvme_show_error("Invalid log size for log id : 0x%02X",nvme_show_message(1, "Invalid log size for log id : 0x%02X", aVendorLogs
[i].ucLogPage)
3943 aVendorLogs[i].ucLogPage)nvme_show_message(1, "Invalid log size for log id : 0x%02X", aVendorLogs
[i].ucLogPage)
;
3944 err = -1;
3945 break;
3946 }
3947 dataBuffer = (unsigned char *)libnvme_alloc(bSize);
3948 if (!dataBuffer) {
3949 nvme_show_error("Memory allocation failed for log id : 0x%02X",nvme_show_message(1, "Memory allocation failed for log id : 0x%02X"
, aVendorLogs[i].ucLogPage)
3950 aVendorLogs[i].ucLogPage)nvme_show_message(1, "Memory allocation failed for log id : 0x%02X"
, aVendorLogs[i].ucLogPage)
;
3951 continue;
3952 }
3953 memcpy(dataBuffer, &stWllHdr,
3954 sizeof(struct MICRON_WORKLOAD_LOG_HDR));
3955 err = NVMEGetLogPage(hdl,
3956 aVendorLogs[i].ucLogPage,
3957 (dataBuffer +
3958 sizeof(struct MICRON_WORKLOAD_LOG_HDR)),
3959 (bSize -
3960 sizeof(struct MICRON_WORKLOAD_LOG_HDR)),
3961 sizeof(struct MICRON_WORKLOAD_LOG_HDR));
3962 if (err != 0)
3963 nvme_show_error("Failed to fetch the E9 logs")nvme_show_message(1, "Failed to fetch the E9 logs");
3964
3965 }
3966 break;
3967 case 0xE2:
3968 if (eModel == M51CX || eModel == M51BY || eModel == M51CY)
3969 continue;
3970
3971 err = get_common_log(hdl, aVendorLogs[i].ucLogPage,
3972 &dataBuffer, &bSize);
3973 break;
3974 case 0xE3:
3975 case 0xE4:
3976 case 0xE8:
3977 case 0xEA:
3978 err = get_common_log(hdl, aVendorLogs[i].ucLogPage,
3979 &dataBuffer, &bSize);
3980 break;
3981 case 0xC1:
3982 case 0xC2:
3983 if (eModel == M51CX || eModel == M51BY || eModel == M51CY)
3984 continue;
3985
3986 err = GetLogPageSize(hdl, aVendorLogs[i].ucLogPage, &bSize);
3987 if (err == 0 && bSize > 0)
3988 err = GetCommonLogPage(hdl,
3989 aVendorLogs[i].ucLogPage,
3990 &dataBuffer, bSize);
3991 break;
3992 case 0xC4:
3993 if (eModel == M51BY || eModel == M51CY)
3994 continue;
3995
3996 err = GetLogPageSize(hdl, aVendorLogs[i].ucLogPage,
3997 &bSize);
3998 if (!err && bSize > 0)
3999 err = GetCommonLogPage(hdl, aVendorLogs[i].ucLogPage,
4000 &dataBuffer, bSize);
4001 break;
4002 case 0xE6:
4003 case 0xE7:
4004 puiIDDBuf = (unsigned int *)&ctrl;
4005 uiMask = puiIDDBuf[1015];
4006 if (!uiMask || (aVendorLogs[i].ucLogPage == 0xE6 && uiMask == 2) ||
4007 (aVendorLogs[i].ucLogPage == 0xE7 && uiMask == 1))
4008 continue;
4009
4010 bSize = (int)puiIDDBuf[1023];
4011 if (bSize % (16 * 1024))
4012 bSize += (16 * 1024) - (bSize % (16 * 1024));
4013 dataBuffer = (unsigned char *)libnvme_alloc(bSize);
4014 if (bSize && dataBuffer) {
4015 memset(dataBuffer, 0, bSize);
4016 if (eModel == M5410 || eModel == M5407)
4017 err = NVMEGetLogPage(hdl,
4018 aVendorLogs[i].ucLogPage,
4019 dataBuffer, bSize, 0);
4020 else
4021 err = nvme_get_log_simple(hdl,
4022 aVendorLogs[i].ucLogPage,
4023 dataBuffer, bSize);
4024 }
4025 break;
4026 case 0xF7:
4027 case 0xF9:
4028 case 0xFC:
4029 case 0xFD:
4030 if (eModel == M51BX)
4031 (void)NVMEResetLog(hdl, aVendorLogs[i].ucLogPage,
4032 aVendorLogs[i].nLogSize, aVendorLogs[i].nMaxSize);
4033
4034 continue;
4035 default:
4036 bSize = aVendorLogs[i].nLogSize;
4037 dataBuffer = (unsigned char *)libnvme_alloc(bSize);
4038 if (!dataBuffer)
4039 break;
4040 memset(dataBuffer, 0, bSize);
4041 err = nvme_get_log_simple(hdl, aVendorLogs[i].ucLogPage,
4042 dataBuffer, bSize);
4043 maxSize = aVendorLogs[i].nMaxSize - bSize;
4044 while (!err && maxSize > 0 && ((unsigned int *)dataBuffer)[0] != 0xdeadbeef) {
4045 snprintf(msg, sizeof(msg), "log 0x%x", aVendorLogs[i].ucLogPage);
4046 WriteData(dataBuffer, bSize, strCtrlDirName, aVendorLogs[i].strFileName, msg);
4047 wrote = true1;
4048 err = nvme_get_log_simple(hdl,
4049 aVendorLogs[i].ucLogPage,
4050 dataBuffer, bSize);
4051 if (err || (((unsigned int *)dataBuffer)[0] == 0xdeadbeef))
4052 break;
4053 maxSize -= bSize;
4054 }
4055 break;
4056 }
4057
4058 if (!err && dataBuffer && ((unsigned int *)dataBuffer)[0] != 0xdeadbeef) {
4059 snprintf(msg, sizeof(msg), "log 0x%x", aVendorLogs[i].ucLogPage);
4060 WriteData(dataBuffer, bSize, strCtrlDirName, aVendorLogs[i].strFileName, msg);
4061 wrote = true1;
4062 }
4063
4064 /*
4065 * Only logs with data on disk are marked available to decode.
4066 * A log that is empty or intentionally skipped gets no row.
4067 */
4068 if (wrote)
4069 LogCmdStatus(aVendorLogs[i].strFileName, 0);
4070 else if (err)
4071 LogCmdStatus(aVendorLogs[i].strFileName, err);
4072
4073 libnvme_free(dataBuffer);
4074 dataBuffer = NULL((void*)0);
4075 }
4076
4077 err = ZipAndRemoveDir(strMainDirName, cfg.package);
4078out:
4079 return err;
4080}
4081
4082#define MIN_LOG_SIZE512 512
4083static int micron_logpage_dir(int argc, char **argv, struct command *acmd,
4084 struct plugin *plugin)
4085{
4086 int err = -1;
4087 const char *desc = "List the supported log pages";
4088 enum eDriveModel model = UNKNOWN_MODEL;
4089 char logbuf[MIN_LOG_SIZE512];
4090 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
4091 __cleanup_nvme_transport_handle__attribute__((cleanup(cleanup_nvme_transport_handle))) struct libnvme_transport_handle *hdl = NULL((void*)0);
4092 int i;
4093
4094 OPT_ARGS(opts)struct argconfig_commandline_options opts[] = {
4095 OPT_END(){ ((void*)0) }
4096 };
4097
4098 err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &model);
4099 if (err)
4100 return err;
4101
4102 struct nvme_supported_logs {
4103 uint8_t log_id;
4104 uint8_t supported;
4105 char *desc;
4106 } log_list[] = {
4107 {0x00, 0, "Supported Log Pages"},
4108 {0x01, 0, "Error Information"},
4109 {0x02, 0, "SMART / Health Information"},
4110 {0x03, 0, "Firmware Slot Information"},
4111 {0x04, 0, "Changed Namespace List"},
4112 {0x05, 0, "Commands Supported and Effects"},
4113 {0x06, 0, "Device Self Test"},
4114 {0x07, 0, "Telemetry Host-Initiated"},
4115 {0x08, 0, "Telemetry Controller-Initiated"},
4116 {0x09, 0, "Endurance Group Information"},
4117 {0x0A, 0, "Predictable Latency Per NVM Set"},
4118 {0x0B, 0, "Predictable Latency Event Aggregate"},
4119 {0x0C, 0, "Asymmetric Namespace Access"},
4120 {0x0D, 0, "Persistent Event Log"},
4121 {0x0E, 0, "LBA Status Information"},
4122 {0x0F, 0, "Endurance Group Event Aggregate"},
4123 {0x10, 0, "Media Unit Status"},
4124 {0x11, 0, "Supported Capacity Configuration List"},
4125 {0x12, 0, "Feature Identifiers Supported and Effects"},
4126 {0x13, 0, "NVMe-MI Commands Supported and Effects"},
4127 {0x14, 0, "Command and Feature lockdown"},
4128 {0x15, 0, "Boot Partition"},
4129 {0x16, 0, "Rotational Media Information"},
4130 {0x70, 0, "Discovery"},
4131 {0x80, 0, "Reservation Notification"},
4132 {0x81, 0, "Sanitize Status"},
4133 {0xC0, 0, "SMART Cloud Health Log"},
4134 {0xC2, 0, "Firmware Activation History"},
4135 {0xC3, 0, "Latency Monitor Log"},
4136 };
4137
4138 printf("Supported log page list\nLog ID : Description\n");
4139 for (i = 0; i < ARRAY_SIZE(log_list)(sizeof(log_list) / sizeof((log_list)[0]) + 0); i++) {
4140 err = nvme_get_log_simple(hdl, log_list[i].log_id,
4141 &logbuf[0], MIN_LOG_SIZE512);
4142 if (err)
4143 continue;
4144 printf("%02Xh : %s\n", log_list[i].log_id, log_list[i].desc);
4145 }
4146
4147 return 0;
4148}
4149
4150static int micron_cloud_boot_SSD_version(int argc, char **argv,
4151 struct command *command, struct plugin *plugin)
4152{
4153 const char *desc = "Prints HyperScale Boot Version";
4154 unsigned char logC0[C0_log_size512] = { 0 };
4155 struct nvme_id_ctrl ctrl;
4156 struct libnvme_passthru_cmd cmd;
4157 enum eDriveModel eModel = UNKNOWN_MODEL;
4158 int err = 0;
4159 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
4160 __cleanup_nvme_transport_handle__attribute__((cleanup(cleanup_nvme_transport_handle))) struct libnvme_transport_handle *hdl = NULL((void*)0);
4161
4162 OPT_ARGS(opts)struct argconfig_commandline_options opts[] = {
4163 OPT_END(){ ((void*)0) }
4164 };
4165
4166 err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &eModel);
4167 if (err)
4168 return err;
4169
4170 nvme_init_identify_ctrl(&cmd, &ctrl);
4171 err = libnvme_exec_admin_passthru(hdl, &cmd);
4172 if (err == 0) {
4173 if (ctrl.vs[536] != MICRON_CUST_ID_GG0x16) {
4174 nvme_show_error("cloud-boot-SSD-version option is not supported for specified drive")nvme_show_message(1, "cloud-boot-SSD-version option is not supported for specified drive"
)
;
4175 err = -ENOTSUP95;
4176 goto out;
4177 }
4178 } else {
4179 nvme_show_error("Error %d retrieving controller identification data", err)nvme_show_message(1, "Error %d retrieving controller identification data"
, err)
;
4180 goto out;
4181 }
4182
4183 err = nvme_get_log_simple(hdl, 0xC0, logC0, C0_log_size512);
4184 if (err == 0) {
4185 __u16 major, minor;
4186
4187 major = *((__u16 *)(logC0+300));
4188 minor = *((__u16 *)(logC0+302));
4189
4190 printf("HyperScale Boot Version Spec.%x.%x\n", le16_to_cpu(major)
4191 , le16_to_cpu(minor));
4192 } else {
4193 nvme_show_err(err, "Error retrieving extended smart log 0xC0 for the drive");
4194 goto out;
4195 }
4196out:
4197 return err;
4198}
4199
4200static int micron_device_waf(int argc, char **argv, struct command *acmd,
4201 struct plugin *plugin)
4202{
4203 const char *desc = "Prints device Write Amplification Factor(WAF)";
4204 unsigned char logC0[C0_log_size512] = { 0 };
4205 struct nvme_id_ctrl ctrl;
4206 struct libnvme_passthru_cmd cmd;
4207 struct nvme_smart_log smart_log;
4208 enum eDriveModel eModel = UNKNOWN_MODEL;
4209 int err = 0;
4210 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
4211 __cleanup_nvme_transport_handle__attribute__((cleanup(cleanup_nvme_transport_handle))) struct libnvme_transport_handle *hdl = NULL((void*)0);
4212
4213 long double tlc_units_written, slc_units_written;
4214 long double data_units_written, write_amplification_factor;
4215
4216 OPT_ARGS(opts)struct argconfig_commandline_options opts[] = {
4217 OPT_END(){ ((void*)0) }
4218 };
4219
4220 err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &eModel);
4221 if (err)
4222 return err;
4223
4224 nvme_init_identify_ctrl(&cmd, &ctrl);
4225 err = libnvme_exec_admin_passthru(hdl, &cmd);
4226 if (err == 0) {
4227 if (ctrl.vs[536] != MICRON_CUST_ID_GG0x16) {
4228 nvme_show_error("vs-device-waf option is not supported for specified drive")nvme_show_message(1, "vs-device-waf option is not supported for specified drive"
)
;
4229 err = -ENOTSUP95;
4230 goto out;
4231 }
4232 } else {
4233 nvme_show_error("Error %d retrieving controller identification data", err)nvme_show_message(1, "Error %d retrieving controller identification data"
, err)
;
4234 goto out;
4235 }
4236
4237 err = nvme_get_log_smart(hdl, NVME_NSID_ALL, &smart_log);
4238 if (err != 0) {
4239 nvme_show_error("nvme_smart_log() failed, err = %d", err)nvme_show_message(1, "nvme_smart_log() failed, err = %d", err
)
;
4240 goto out;
4241 }
4242
4243 err = nvme_get_log_simple(hdl, 0xC0, logC0, C0_log_size512);
4244 if (err != 0) {
4245 nvme_show_error("Failed to get extended smart log, err = %d", err)nvme_show_message(1, "Failed to get extended smart log, err = %d"
, err)
;
4246 goto out;
4247 }
4248
4249 data_units_written = int128_to_double(smart_log.data_units_written);
4250 tlc_units_written = int128_to_double((__u8 *)logC0);
4251 slc_units_written = int128_to_double((__u8 *)(logC0+16));
4252 write_amplification_factor = (data_units_written/(tlc_units_written + slc_units_written));
4253 printf("Write Amplification Factor %.0Lf\n", write_amplification_factor);
4254
4255out:
4256 return err;
4257}
4258
4259static int micron_cloud_log(int argc, char **argv, struct command *acmd,
4260 struct plugin *plugin)
4261{
4262 const char *desc = "Retrieve Smart or Extended Smart Health log for the given device ";
4263 unsigned int logC0[C0_log_size512/sizeof(int)] = { 0 };
4264 struct nvme_id_ctrl ctrl;
4265 struct libnvme_passthru_cmd cmd;
4266 enum eDriveModel eModel = UNKNOWN_MODEL;
4267 int err = 0;
4268 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
4269 __cleanup_nvme_transport_handle__attribute__((cleanup(cleanup_nvme_transport_handle))) struct libnvme_transport_handle *hdl = NULL((void*)0);
4270 bool_Bool is_json = false0;
4271 nvme_print_flags_t format;
4272
4273 OPT_ARGS(opts)struct argconfig_commandline_options opts[] = {
4274 OPT_END(){ ((void*)0) }
4275 };
4276
4277 err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &eModel);
4278 if (err)
4279 return err;
4280
4281 format = nvme_args.output_flags;
4282 is_json = format == JSON;
4283
4284 /* check for models that support 0xC0 log */
4285 if (eModel != M51CX) {
4286 nvme_show_error("Unsupported drive model for vs-cloud-log commmand")nvme_show_message(1, "Unsupported drive model for vs-cloud-log commmand"
)
;
4287 err = -1;
4288 goto out;
4289 }
4290
4291 nvme_init_identify_ctrl(&cmd, &ctrl);
4292 err = libnvme_exec_admin_passthru(hdl, &cmd);
4293 if (err == 0) {
4294 if (ctrl.vs[536] != MICRON_CUST_ID_GG0x16) {
4295 nvme_show_error("vs-cloud-log option is not supported for specified drive")nvme_show_message(1, "vs-cloud-log option is not supported for specified drive"
)
;
4296 err = -ENOTSUP95;
4297 goto out;
4298 }
4299 } else {
4300 nvme_show_error("Error %d retrieving controller identification data", err)nvme_show_message(1, "Error %d retrieving controller identification data"
, err)
;
4301 goto out;
4302 }
4303
4304 err = nvme_get_log_simple(hdl, 0xC0, logC0, C0_log_size512);
4305 if (err == 0)
4306 print_hyperscale_cloud_health_log((__u8 *)logC0, is_json);
4307 else
4308 nvme_show_error("Unable to retrieve extended smart log 0xC0 for the drive")nvme_show_message(1, "Unable to retrieve extended smart log 0xC0 for the drive"
)
;
4309
4310out:
4311 if (err > 0)
4312 nvme_show_status(err);
4313 return err;
4314}
4315
4316/* Extended SMART log structure with Micron-specific fields in reserved area */
4317struct micron_smart_log_ext {
4318 struct nvme_smart_log base;
4319 /* Access vendor-specific fields via rsvd232 overlay */
4320};
4321
4322static inline __u64 get_smart_olec(struct nvme_smart_log *smart)
4323{
4324 return le64_to_cpu(smart->op_lifetime_energy_consumed);
4325}
4326
4327static inline __u32 get_smart_ipm(struct nvme_smart_log *smart)
4328{
4329 return le32_to_cpu(smart->interval_power_measurement);
4330}
4331
4332static void print_micron_health_log_normal(struct nvme_smart_log *smart,
4333 const char *devname)
4334{
4335 __u16 temp = smart->temperature[1] << 8 | smart->temperature[0];
4336 __u64 olec = get_smart_olec(smart);
4337 __u32 ipm = get_smart_ipm(smart);
4338 int i;
4339
4340 printf("SMART/Health Information Log for %s\n", devname);
4341 printf("========================================\n");
4342
4343 printf("Critical Warning : 0x%02x\n",
4344 smart->critical_warning);
4345 if (smart->critical_warning) {
4346 if (smart->critical_warning & 0x01)
4347 printf(" - Available spare below threshold\n");
4348 if (smart->critical_warning & 0x02)
4349 printf(" - Temperature threshold exceeded\n");
4350 if (smart->critical_warning & 0x04)
4351 printf(" - NVM subsystem reliability degraded\n");
4352 if (smart->critical_warning & 0x08)
4353 printf(" - Media placed in read-only mode\n");
4354 if (smart->critical_warning & 0x10)
4355 printf(" - Volatile memory backup failed\n");
4356 if (smart->critical_warning & 0x20)
4357 printf(" - PMR read-only or unreliable\n");
4358 }
4359
4360 printf("Composite Temperature : %u K (%d C)\n",
4361 temp, temp ? temp - 273 : 0);
4362 printf("Available Spare : %u%%\n", smart->avail_spare);
4363 printf("Available Spare Threshold : %u%%\n", smart->spare_thresh);
4364 printf("Percentage Used : %u%%\n", smart->percent_used);
4365 printf("Endurance Grp Critical Warn : 0x%02x\n",
4366 smart->endu_grp_crit_warn_sumry);
4367
4368 printf("Data Units Read : %s\n",
4369 uint128_t_to_string(le128_to_cpu(smart->data_units_read)));
4370 printf("Data Units Written : %s\n",
4371 uint128_t_to_string(le128_to_cpu(smart->data_units_written)));
4372 printf("Host Read Commands : %s\n",
4373 uint128_t_to_string(le128_to_cpu(smart->host_reads)));
4374 printf("Host Write Commands : %s\n",
4375 uint128_t_to_string(le128_to_cpu(smart->host_writes)));
4376 printf("Controller Busy Time : %s min\n",
4377 uint128_t_to_string(le128_to_cpu(smart->ctrl_busy_time)));
4378 printf("Power Cycles : %s\n",
4379 uint128_t_to_string(le128_to_cpu(smart->power_cycles)));
4380 printf("Power On Hours : %s\n",
4381 uint128_t_to_string(le128_to_cpu(smart->power_on_hours)));
4382 printf("Unsafe Shutdowns : %s\n",
4383 uint128_t_to_string(le128_to_cpu(smart->unsafe_shutdowns)));
4384 printf("Media Errors : %s\n",
4385 uint128_t_to_string(le128_to_cpu(smart->media_errors)));
4386 printf("Num Error Log Entries : %s\n",
4387 uint128_t_to_string(le128_to_cpu(smart->num_err_log_entries)));
4388
4389 printf("Warning Comp Temp Time : %u min\n",
4390 le32_to_cpu(smart->warning_temp_time));
4391 printf("Critical Comp Temp Time : %u min\n",
4392 le32_to_cpu(smart->critical_comp_time));
4393
4394 for (i = 0; i < 8; i++) {
4395 __u16 ts = le16_to_cpu(smart->temp_sensor[i]);
4396
4397 if (ts)
4398 printf("Temperature Sensor %d : %u K (%d C)\n",
4399 i + 1, ts, ts - 273);
4400 }
4401
4402 printf("Thm Temp 1 Trans Count : %u\n",
4403 le32_to_cpu(smart->thm_temp1_trans_count));
4404 printf("Thm Temp 2 Trans Count : %u\n",
4405 le32_to_cpu(smart->thm_temp2_trans_count));
4406 printf("Thm Temp 1 Total Time : %u sec\n",
4407 le32_to_cpu(smart->thm_temp1_total_time));
4408 printf("Thm Temp 2 Total Time : %u sec\n",
4409 le32_to_cpu(smart->thm_temp2_total_time));
4410
4411 /* Micron-specific extended fields */
4412 printf("OLEC (Energy) : %llu\n",
4413 (unsigned long long)olec);
4414 printf("Interval Power Measurement : %u\n", ipm);
4415}
4416
4417static void print_micron_health_log_json(struct nvme_smart_log *smart,
4418 const char *devname)
4419{
4420 __u16 temp = smart->temperature[1] << 8 | smart->temperature[0];
4421 __u64 olec = get_smart_olec(smart);
4422 __u32 ipm = get_smart_ipm(smart);
4423 struct json_object *root;
4424 int i;
4425
4426 root = json_create_object()json_object_new_object();
4427
4428 json_object_add_value_string(root, "device", devname);
4429 json_object_add_value_int(root, "critical_warning",json_object_object_add(root, "critical_warning", json_object_new_int
(smart->critical_warning))
4430 smart->critical_warning)json_object_object_add(root, "critical_warning", json_object_new_int
(smart->critical_warning))
;
4431 json_object_add_value_int(root, "temperature_kelvin", temp)json_object_object_add(root, "temperature_kelvin", json_object_new_int
(temp))
;
4432 json_object_add_value_int(root, "temperature_celsius",json_object_object_add(root, "temperature_celsius", json_object_new_int
(temp ? temp - 273 : 0))
4433 temp ? temp - 273 : 0)json_object_object_add(root, "temperature_celsius", json_object_new_int
(temp ? temp - 273 : 0))
;
4434 json_object_add_value_int(root, "avail_spare", smart->avail_spare)json_object_object_add(root, "avail_spare", json_object_new_int
(smart->avail_spare))
;
4435 json_object_add_value_int(root, "spare_thresh", smart->spare_thresh)json_object_object_add(root, "spare_thresh", json_object_new_int
(smart->spare_thresh))
;
4436 json_object_add_value_int(root, "percent_used", smart->percent_used)json_object_object_add(root, "percent_used", json_object_new_int
(smart->percent_used))
;
4437 json_object_add_value_int(root, "endurance_grp_crit_warn",json_object_object_add(root, "endurance_grp_crit_warn", json_object_new_int
(smart->endu_grp_crit_warn_sumry))
4438 smart->endu_grp_crit_warn_sumry)json_object_object_add(root, "endurance_grp_crit_warn", json_object_new_int
(smart->endu_grp_crit_warn_sumry))
;
4439
4440 json_object_add_value_string(root, "data_units_read",
4441 uint128_t_to_string(le128_to_cpu(smart->data_units_read)));
4442 json_object_add_value_string(root, "data_units_written",
4443 uint128_t_to_string(le128_to_cpu(smart->data_units_written)));
4444 json_object_add_value_string(root, "host_reads",
4445 uint128_t_to_string(le128_to_cpu(smart->host_reads)));
4446 json_object_add_value_string(root, "host_writes",
4447 uint128_t_to_string(le128_to_cpu(smart->host_writes)));
4448 json_object_add_value_string(root, "ctrl_busy_time",
4449 uint128_t_to_string(le128_to_cpu(smart->ctrl_busy_time)));
4450 json_object_add_value_string(root, "power_cycles",
4451 uint128_t_to_string(le128_to_cpu(smart->power_cycles)));
4452 json_object_add_value_string(root, "power_on_hours",
4453 uint128_t_to_string(le128_to_cpu(smart->power_on_hours)));
4454 json_object_add_value_string(root, "unsafe_shutdowns",
4455 uint128_t_to_string(le128_to_cpu(smart->unsafe_shutdowns)));
4456 json_object_add_value_string(root, "media_errors",
4457 uint128_t_to_string(le128_to_cpu(smart->media_errors)));
4458 json_object_add_value_string(root, "num_err_log_entries",
4459 uint128_t_to_string(le128_to_cpu(smart->num_err_log_entries)));
4460
4461 json_object_add_value_uint(root, "warning_temp_time",json_object_object_add(root, "warning_temp_time", json_object_new_uint64
(le32_to_cpu(smart->warning_temp_time)))
4462 le32_to_cpu(smart->warning_temp_time))json_object_object_add(root, "warning_temp_time", json_object_new_uint64
(le32_to_cpu(smart->warning_temp_time)))
;
4463 json_object_add_value_uint(root, "critical_comp_time",json_object_object_add(root, "critical_comp_time", json_object_new_uint64
(le32_to_cpu(smart->critical_comp_time)))
4464 le32_to_cpu(smart->critical_comp_time))json_object_object_add(root, "critical_comp_time", json_object_new_uint64
(le32_to_cpu(smart->critical_comp_time)))
;
4465
4466 for (i = 0; i < 8; i++) {
4467 __u16 ts = le16_to_cpu(smart->temp_sensor[i]);
4468 char key[32];
4469
4470 if (ts) {
4471 snprintf(key, sizeof(key), "temp_sensor_%d", i + 1);
4472 json_object_add_value_int(root, key, ts - 273)json_object_object_add(root, key, json_object_new_int(ts - 273
))
;
4473 }
4474 }
4475
4476 json_object_add_value_uint(root, "thm_temp1_trans_count",json_object_object_add(root, "thm_temp1_trans_count", json_object_new_uint64
(le32_to_cpu(smart->thm_temp1_trans_count)))
4477 le32_to_cpu(smart->thm_temp1_trans_count))json_object_object_add(root, "thm_temp1_trans_count", json_object_new_uint64
(le32_to_cpu(smart->thm_temp1_trans_count)))
;
4478 json_object_add_value_uint(root, "thm_temp2_trans_count",json_object_object_add(root, "thm_temp2_trans_count", json_object_new_uint64
(le32_to_cpu(smart->thm_temp2_trans_count)))
4479 le32_to_cpu(smart->thm_temp2_trans_count))json_object_object_add(root, "thm_temp2_trans_count", json_object_new_uint64
(le32_to_cpu(smart->thm_temp2_trans_count)))
;
4480 json_object_add_value_uint(root, "thm_temp1_total_time",json_object_object_add(root, "thm_temp1_total_time", json_object_new_uint64
(le32_to_cpu(smart->thm_temp1_total_time)))
4481 le32_to_cpu(smart->thm_temp1_total_time))json_object_object_add(root, "thm_temp1_total_time", json_object_new_uint64
(le32_to_cpu(smart->thm_temp1_total_time)))
;
4482 json_object_add_value_uint(root, "thm_temp2_total_time",json_object_object_add(root, "thm_temp2_total_time", json_object_new_uint64
(le32_to_cpu(smart->thm_temp2_total_time)))
4483 le32_to_cpu(smart->thm_temp2_total_time))json_object_object_add(root, "thm_temp2_total_time", json_object_new_uint64
(le32_to_cpu(smart->thm_temp2_total_time)))
;
4484
4485 /* Micron-specific extended fields */
4486 json_object_add_value_uint64(root, "olec", olec)json_object_object_add(root, "olec", json_object_new_uint64(olec
))
;
4487 json_object_add_value_uint(root, "ipm", ipm)json_object_object_add(root, "ipm", json_object_new_uint64(ipm
))
;
4488
4489 json_print_object(root, NULL)printf("%s", json_object_to_json_string_ext(root, (1 <<
1) | (1 << 4)))
;
4490 printf("\n");
4491 json_free_object(root)json_object_put(root);
4492}
4493
4494static int micron_health_info(int argc, char **argv, struct command *acmd,
4495 struct plugin *plugin)
4496{
4497 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
4498 __cleanup_nvme_transport_handle__attribute__((cleanup(cleanup_nvme_transport_handle))) struct libnvme_transport_handle *hdl = NULL((void*)0);
4499 const char *desc = "Retrieve SMART/Health log for Micron drives";
4500 enum eDriveModel eModel = UNKNOWN_MODEL;
4501 struct nvme_smart_log smart_log = { 0 };
4502 bool_Bool is_json = false0;
4503 nvme_print_flags_t format;
4504 int err = 0;
4505
4506 OPT_ARGS(opts)struct argconfig_commandline_options opts[] = {
4507 OPT_END(){ ((void*)0) }
4508 };
4509
4510 err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &eModel);
4511 if (err)
4512 return err;
4513
4514 /* Not an error: keep it off stdout so JSON output stays parseable */
4515 if (eModel == UNKNOWN_MODEL)
4516 fprintf(stderrstderr, "WARNING: Unknown drive model\n");
4517
4518 format = nvme_args.output_flags;
4519 is_json = format == JSON;
4520
4521 err = nvme_get_log_smart(hdl, NVME_NSID_ALL, &smart_log);
4522 if (err) {
4523 nvme_show_error("Failed to get SMART log: %s",nvme_show_message(1, "Failed to get SMART log: %s", libnvme_strerror
(err))
4524 libnvme_strerror(err))nvme_show_message(1, "Failed to get SMART log: %s", libnvme_strerror
(err))
;
4525 return err;
4526 }
4527
4528 if (is_json)
4529 print_micron_health_log_json(&smart_log, nvme_args.device);
4530 else
4531 print_micron_health_log_normal(&smart_log, nvme_args.device);
4532
4533 return 0;
4534}
4535
4536/*
4537 * Identify Controller field offsets for Micron-specific fields
4538 * PMS: Power Measurement Support - bit 21 of CTRATT
4539 */
4540#define CTRATT_PMS_BIT21 21
4541
4542static inline bool_Bool get_id_ctrl_pms(struct nvme_id_ctrl *ctrl)
4543{
4544 return (le32_to_cpu(ctrl->ctratt) >> CTRATT_PMS_BIT21) & 0x1;
4545}
4546
4547/* Micron vendor-specific id-ctrl fields display */
4548static void micron_id_ctrl_vs(__u8 *vs, struct json_object *root)
4549{
4550 /* Cast back to get full ctrl structure for our extended fields */
4551 struct nvme_id_ctrl *ctrl =
4552 (struct nvme_id_ctrl *)(vs - offsetof(struct nvme_id_ctrl, vs)__builtin_offsetof(struct nvme_id_ctrl, vs));
4553 bool_Bool pms = get_id_ctrl_pms(ctrl);
4554
4555 if (root)
4556 /* JSON output */
4557 json_object_add_value_int(root, "pms", pms ? 1 : 0)json_object_object_add(root, "pms", json_object_new_int(pms ?
1 : 0))
;
4558 else
4559 /* Normal output */
4560 printf("pms : %u\n", pms ? 1 : 0);
4561}
4562
4563static int micron_id_ctrl(int argc, char **argv, struct command *acmd,
4564 struct plugin *plugin)
4565{
4566 __cleanup_nvme_global_ctx__attribute__((cleanup(cleanup_nvme_global_ctx))) struct libnvme_global_ctx *ctx = NULL((void*)0);
4567 __cleanup_nvme_transport_handle__attribute__((cleanup(cleanup_nvme_transport_handle))) struct libnvme_transport_handle *hdl = NULL((void*)0);
4568 const char *desc = "Identify Controller with Micron vendor fields";
4569 enum eDriveModel eModel = UNKNOWN_MODEL;
4570 struct nvme_id_ctrl ctrl = { 0 };
4571 struct libnvme_passthru_cmd cmd;
4572 nvme_print_flags_t flags;
4573 int err = 0;
4574
4575 OPT_ARGS(opts)struct argconfig_commandline_options opts[] = {
4576 OPT_END(){ ((void*)0) }
4577 };
4578
4579 err = micron_parse_options(&ctx, &hdl, argc, argv, desc, opts, &eModel);
4580 if (err)
4581 return err;
4582
4583 /* Not an error: keep it out of the id-ctrl JSON object */
4584 if (eModel == UNKNOWN_MODEL)
4585 fprintf(stderrstderr,
4586 "WARNING: Drive not recognized as Micron, proceeding anyway\n");
4587
4588 flags = nvme_args.output_flags;
4589
4590 nvme_init_identify_ctrl(&cmd, &ctrl);
4591 err = libnvme_exec_admin_passthru(hdl, &cmd);
4592 if (err) {
4593 nvme_show_error("identify controller failed: %s",nvme_show_message(1, "identify controller failed: %s", libnvme_strerror
(err))
4594 libnvme_strerror(err))nvme_show_message(1, "identify controller failed: %s", libnvme_strerror
(err))
;
4595 return err;
4596 }
4597
4598 nvme_show_id_ctrl(ctx, hdl, &ctrl, flags, micron_id_ctrl_vs);
4599
4600 return 0;
4601}
4602
4603static struct command select_download_cmd = {
4604 .name = "select-download",
4605 .help = "Selective Firmware Download",
4606 .fn = micron_selective_download,
4607};
4608
4609static struct command vs_temperature_stats_cmd = {
4610 .name = "vs-temperature-stats",
4611 .help = "Retrieve Micron temperature statistics ",
4612 .fn = micron_temp_stats,
4613 .output_formats = NORMAL | JSON,
4614};
4615
4616static struct command vs_pcie_stats_cmd = {
4617 .name = "vs-pcie-stats",
4618 .help = "Retrieve Micron PCIe error stats",
4619 .fn = micron_pcie_stats,
4620 .output_formats = NORMAL | JSON,
4621 .needs_root = true1,
4622};
4623
4624static struct command clear_pcie_correctable_errors_cmd = {
4625 .name = "clear-pcie-correctable-errors",
4626 .help = "Clear correctable PCIe errors",
4627 .fn = micron_clear_pcie_correctable_errors,
4628 .needs_root = true1,
4629};
4630
4631static struct command vs_internal_log_cmd = {
4632 .name = "vs-internal-log",
4633 .help = "Retrieve Micron logs",
4634 .fn = micron_internal_logs,
4635 .needs_root = true1,
4636};
4637
4638static struct command vs_telemetry_controller_option_cmd = {
4639 .name = "vs-telemetry-controller-option",
4640 .help = "Enable/Disable controller telemetry log generation",
4641 .fn = micron_telemetry_cntrl_option,
4642};
4643
4644static struct command vs_nand_stats_cmd = {
4645 .name = "vs-nand-stats",
4646 .help = "Retrieve NAND Stats",
4647 .fn = micron_nand_stats,
4648 .output_formats = NORMAL | JSON,
4649};
4650
4651static struct command vs_smart_ext_log_cmd = {
4652 .name = "vs-smart-ext-log",
4653 .help = "Retrieve extended SMART logs",
4654 .fn = micron_smart_ext_log,
4655 .output_formats = NORMAL | JSON,
4656};
4657
4658static struct command vs_drive_info_cmd = {
4659 .name = "vs-drive-info",
4660 .help = "Retrieve Drive information",
4661 .fn = micron_drive_info,
4662 .output_formats = NORMAL | JSON,
4663};
4664
4665static struct command plugin_version_cmd = {
4666 .name = "plugin-version",
4667 .help = "Display plugin version info",
4668 .fn = micron_plugin_version,
4669 .device = COMMAND_NO_DEVICE,
4670 .needs_root = true1,
4671};
4672
4673static struct command cloud_ssd_plugin_version_cmd = {
4674 .name = "cloud-SSD-plugin-version",
4675 .help = "Display plugin version info",
4676 .fn = micron_cloud_ssd_plugin_version,
4677 .device = COMMAND_NO_DEVICE,
4678 .needs_root = true1,
4679};
4680
4681static struct command log_page_directory_cmd = {
4682 .name = "log-page-directory",
4683 .help = "Retrieve log page directory",
4684 .fn = micron_logpage_dir,
4685};
4686
4687static struct command vs_fw_activate_history_cmd = {
4688 .name = "vs-fw-activate-history",
4689 .help = "Display FW activation history",
4690 .fn = micron_fw_activation_history,
4691 .output_formats = NORMAL | JSON,
4692};
4693
4694static struct command latency_tracking_cmd = {
4695 .name = "latency-tracking",
4696 .help = "Latency monitoring feature control",
4697 .fn = micron_latency_stats_track,
4698};
4699
4700static struct command latency_stats_cmd = {
4701 .name = "latency-stats",
4702 .help = "Latency information for tracked commands",
4703 .fn = micron_latency_stats_info,
4704};
4705
4706static struct command latency_logs_cmd = {
4707 .name = "latency-logs",
4708 .help = "Latency log details tracked by drive",
4709 .fn = micron_latency_stats_logs,
4710};
4711
4712static struct command vs_smart_add_log_cmd = {
4713 .name = "vs-smart-add-log",
4714 .help = "Retrieve extended SMART data",
4715 .fn = micron_ocp_smart_health_logs,
4716 .output_formats = NORMAL | JSON,
4717};
4718
4719static struct command clear_fw_activate_history_cmd = {
4720 .name = "clear-fw-activate-history",
4721 .help = "Clear FW activation history",
4722 .fn = micron_clr_fw_activation_history,
4723};
4724
4725static struct command vs_smbus_option_cmd = {
4726 .name = "vs-smbus-option",
4727 .help = "Enable/Disable SMBUS on the drive",
4728 .fn = micron_smbus_option,
4729};
4730
4731static struct command cloud_boot_ssd_version_cmd = {
4732 .name = "cloud-boot-SSD-version",
4733 .help = "Prints HyperScale Boot Version",
4734 .fn = micron_cloud_boot_SSD_version,
4735};
4736
4737static struct command vs_device_waf_cmd = {
4738 .name = "vs-device-waf",
4739 .help = "Reports SLC and TLC WAF ratio",
4740 .fn = micron_device_waf,
4741};
4742
4743static struct command vs_cloud_log_cmd = {
4744 .name = "vs-cloud-log",
4745 .help = "Retrieve Extended Health Information of Hyperscale NVMe Boot SSD",
4746 .fn = micron_cloud_log,
4747 .output_formats = NORMAL | JSON,
4748};
4749
4750static struct command vs_work_load_log_cmd = {
4751 .name = "vs-work-load-log",
4752 .help = "Retrieve Workload logs",
4753 .fn = micron_work_load_log,
4754 .output_formats = NORMAL | JSON,
4755};
4756
4757static struct command vs_vendor_telemetry_log_cmd = {
4758 .name = "vs-vendor-telemetry-log",
4759 .help = "Retrieve Vendor Telemetry logs",
4760 .fn = micron_vendor_telemetry_log,
4761 .output_formats = NORMAL | JSON,
4762};
4763
4764static struct command smart_log_cmd = {
4765 .name = "smart-log",
4766 .help = "Retrieve SMART/Health Log",
4767 .fn = micron_health_info,
4768 .output_formats = NORMAL | JSON,
4769};
4770
4771static struct command id_ctrl_cmd = {
4772 .name = "id-ctrl",
4773 .help = "Identify Controller",
4774 .fn = micron_id_ctrl,
4775};
4776
4777static struct command *commands[] = {
4778 &select_download_cmd,
4779 &vs_temperature_stats_cmd,
4780 &vs_pcie_stats_cmd,
4781 &clear_pcie_correctable_errors_cmd,
4782 &vs_internal_log_cmd,
4783 &vs_telemetry_controller_option_cmd,
4784 &vs_nand_stats_cmd,
4785 &vs_smart_ext_log_cmd,
4786 &vs_drive_info_cmd,
4787 &plugin_version_cmd,
4788 &cloud_ssd_plugin_version_cmd,
4789 &log_page_directory_cmd,
4790 &vs_fw_activate_history_cmd,
4791 &latency_tracking_cmd,
4792 &latency_stats_cmd,
4793 &latency_logs_cmd,
4794 &vs_smart_add_log_cmd,
4795 &clear_fw_activate_history_cmd,
4796 &vs_smbus_option_cmd,
4797 &cloud_boot_ssd_version_cmd,
4798 &vs_device_waf_cmd,
4799 &vs_cloud_log_cmd,
4800 &vs_work_load_log_cmd,
4801 &vs_vendor_telemetry_log_cmd,
4802 &smart_log_cmd,
4803 &id_ctrl_cmd,
4804 NULL((void*)0),
4805};
4806
4807static struct plugin plugin = {
4808 .name = "micron",
4809 .desc = "Micron vendor specific extensions",
4810 .version = NVME_VERSION"3.1",
4811};
4812
4813static void __shr_constructor__attribute__((constructor)) register_plugin(void)
4814{
4815 plugin_add_group(&plugin, NULL((void*)0), commands);
4816 register_extension(&plugin);
4817}