From abfd028d41e5fc3d6c977d9c758a4f3354cb439f Mon Sep 17 00:00:00 2001 From: Hanumanthu H Date: Thu, 30 Jul 2020 22:28:26 +0530 Subject: [PATCH] Micron plugin and documentation updates --- plugins/micron/micron-nvme.c | 2729 +++++++++++++++++++++------------- plugins/micron/micron-nvme.h | 9 + 2 files changed, 1695 insertions(+), 1043 deletions(-) diff --git a/plugins/micron/micron-nvme.c b/plugins/micron/micron-nvme.c index 165fcf09..f2ba9412 100644 --- a/plugins/micron/micron-nvme.c +++ b/plugins/micron/micron-nvme.c @@ -20,1161 +20,1804 @@ #define min(x, y) ((x) > (y) ? (y) : (x)) #define SensorCount 2 #define C5_log_size (((452 + 16 * 1024) / 4) * 4096) -#define D0_log_size 256 +#define D0_log_size 512 +#define FB_log_size 512 #define MaxLogChunk 16 * 1024 #define CommonChunkSize 16 * 4096 -typedef struct _LogPageHeader_t { - unsigned char numDwordsInLogPageHeaderLo; - unsigned char logPageHeaderFormatVersion; - unsigned char logPageId; - unsigned char numDwordsInLogPageHeaderHi; - unsigned int numValidDwordsInPayload; - unsigned int numDwordsInEntireLogPage; -} LogPageHeader_t; +/* Plugin version major_number.minor_number.patch */ +static const char *__version_major = "1"; +static const char *__version_minor = "0"; +static const char *__version_patch = "0"; -/* - * Useful Helper functions +/* supported models of micron plugin; new models should be added at the end + * before UNKNOWN_MODEL. Make sure M5410 is first in the list ! */ +typedef enum { M5410 = 0, M51AX, M51BX, M51CX, M5407, M5411, UNKNOWN_MODEL } eDriveModel; -static int micron_fw_commit(int fd, int select) -{ - struct nvme_admin_cmd cmd = { - .opcode = nvme_admin_activate_fw, - .cdw10 = 8, - .cdw12 = select, - }; - return ioctl(fd, NVME_IOCTL_ADMIN_CMD, &cmd); +#define MICRON_VENDOR_ID 0x1344 +static char *fvendorid1 = "/sys/class/nvme/nvme%d/device/vendor"; +static char *fvendorid2 = "/sys/class/misc/nvme%d/device/vendor"; +static char *fdeviceid1 = "/sys/class/nvme/nvme%d/device/device"; +static char *fdeviceid2 = "/sys/class/misc/nvme%d/device/device"; +static unsigned short vendor_id; +static unsigned short device_id; + +typedef struct _LogPageHeader_t { + unsigned char numDwordsInLogPageHeaderLo; + unsigned char logPageHeaderFormatVersion; + unsigned char logPageId; + unsigned char numDwordsInLogPageHeaderHi; + unsigned int numValidDwordsInPayload; + unsigned int numDwordsInEntireLogPage; +} LogPageHeader_t; + +static void WriteData(__u8 *data, __u32 len, const char *dir, const char *file, const char *msg) +{ + char tempFolder[PATH_MAX] = { 0 }; + FILE *fpOutFile = NULL; + sprintf(tempFolder, "%s/%s", dir, file); + if ((fpOutFile = fopen(tempFolder, "ab+")) != NULL) { + if (fwrite(data, 1, len, fpOutFile) != len) { + printf("Failed to write %s data to %s\n", msg, tempFolder); + } + fclose(fpOutFile); + } else { + printf("Failed to open %s file to write %s\n", tempFolder, msg); + } } -static int ZipAndRemoveDir(char *strDirName, char *strFileName) +static int ReadSysFile(const char *file, unsigned short *id) { - int err = 0; - char strBuffer[PATH_MAX]; - int nRet; + int ret = 0; + char idstr[32] = { '\0' }; + int fd = open(file, O_RDONLY); - sprintf(strBuffer, "zip -r \"%s\" \"%s\" >temp.txt 2>&1", strFileName, - strDirName); + if (fd > 0) { + ret = read(fd, idstr, sizeof(idstr)); + close(fd); + } - nRet = system(strBuffer); + if (fd < 0 || ret < 0) + perror(file); + else + *id = strtol(idstr, NULL, 16); - if (nRet < 0) { - printf("Unable to create zip package!\n"); - err = EINVAL; - goto exit_status; - } + return ret; +} - sprintf(strBuffer, "rm -f -R \"%s\" >temp.txt 2>&1", strDirName); - nRet = system(strBuffer); - if (nRet < 0) { - printf("Unable to remove temporary files!\n"); - err = EINVAL; - goto exit_status; - } +static eDriveModel GetDriveModel(int idx) +{ + eDriveModel eModel = UNKNOWN_MODEL; + char path[512]; + + sprintf(path, fvendorid1, idx); + if (ReadSysFile(path, &vendor_id) < 0) { + sprintf(path, fvendorid2, idx); + ReadSysFile(path, &vendor_id); + } + sprintf(path, fdeviceid1, idx); + if (ReadSysFile(path, &device_id) < 0) { + sprintf(path, fdeviceid2, idx); + ReadSysFile(path, &device_id); + } + if (vendor_id == MICRON_VENDOR_ID) { + switch (device_id) { + case 0x51A0: + case 0x51A1: + case 0x51A2: + eModel = M51AX; + break; + case 0x51B0: + case 0x51B1: + case 0x51B2: + eModel = M51BX; + break; + case 0x51C0: + case 0x51C1: + eModel = M51CX; + break; + case 0x5405: + case 0x5406: + case 0x5407: + eModel = M5407; + break; + case 0x5410: + eModel = M5410; + break; + case 0x5411: + eModel = M5411; + break; + default: + break; + } + } + return eModel; +} - exit_status: - err = system("rm -f temp.txt"); - return err; +static int ZipAndRemoveDir(char *strDirName, char *strFileName) +{ + int err = 0; + char strBuffer[PATH_MAX]; + int nRet; + + sprintf(strBuffer, "zip -r \"%s\" \"%s\" >temp.txt 2>&1", strFileName, + strDirName); + + nRet = system(strBuffer); + + if (nRet < 0) { + printf("Failed to create zip package!\n"); + err = EINVAL; + goto exit_status; + } + + sprintf(strBuffer, "rm -f -R \"%s\" >temp.txt 2>&1", strDirName); + nRet = system(strBuffer); + if (nRet < 0) { + printf("Failed to remove temporary files!\n"); + err = EINVAL; + goto exit_status; + } + +exit_status: + err = system("rm -f temp.txt"); + return err; } static int SetupDebugDataDirectories(char *strSN, char *strFilePath, - char *strMainDirName, char *strOSDirName, - char *strCtrlDirName) + char *strMainDirName, char *strOSDirName, + char *strCtrlDirName) { - int err = 0; - char strAppend[250]; - struct stat st; - char *fileLocation = NULL; - char *fileName; - int length = 0; - int nIndex = 0; - char *strTemp = NULL; - struct stat dirStat; - int j; - int k = 0; - int i = 0; - - if (strchr(strFilePath, '/') != NULL) { - fileName = strrchr(strFilePath, '\\'); - if (fileName == NULL) { - fileName = strrchr(strFilePath, '/'); - } - - if (fileName != NULL) { - if (!strcmp(fileName, "/")) { - goto exit_status; - } - - while (strFilePath[nIndex] != '\0') { - if ('\\' == strFilePath[nIndex] && '\\' == strFilePath[nIndex + 1]) { - goto exit_status; - } - nIndex++; - } - - length = (int)strlen(strFilePath) - (int)strlen(fileName); - - if (fileName == strFilePath) { - length = 1; - } - - fileLocation = (char *)malloc(length + 1); - strncpy(fileLocation, strFilePath, length); - fileLocation[length] = '\0'; - - while (fileLocation[k] != '\0') { - if (fileLocation[k] == '\\') { - fileLocation[k] = '/'; - } - } - - length = (int)strlen(fileLocation); - - if (':' == fileLocation[length - 1]) { - strTemp = (char *)malloc(length + 2); - strcpy(strTemp, fileLocation); - strcat(strTemp, "/"); - free(fileLocation); - - length = (int)strlen(strTemp); - fileLocation = (char *)malloc(length + 1); - memcpy(fileLocation, strTemp, length + 1); - free(strTemp); - } - - if (stat(fileLocation, &st) != 0) { - free(fileLocation); - goto exit_status; - } - free(fileLocation); - } else { - goto exit_status; - } - } - - nIndex = 0; - for (i = 0; i < (int)strlen(strSN); i++) { - if (strSN[i] != ' ' && strSN[i] != '\n' && strSN[i] != '\t' && strSN[i] != '\r') { - strMainDirName[nIndex++] = strSN[i]; - } - } - strMainDirName[nIndex] = '\0'; - - j = 1; - while (stat(strMainDirName, &dirStat) == 0) { - strMainDirName[nIndex] = '\0'; - sprintf(strAppend, "-%d", j); - strcat(strMainDirName, strAppend); - j++; - } - - mkdir(strMainDirName, 0777); - - if (strOSDirName != NULL) { - sprintf(strOSDirName, "%s/%s", strMainDirName, "OS"); - mkdir(strOSDirName, 0777); - - } - if (strCtrlDirName != NULL) { - sprintf(strCtrlDirName, "%s/%s", strMainDirName, "Controller"); - mkdir(strCtrlDirName, 0777); - - } - - exit_status: - return err; + int err = 0; + char strAppend[250]; + struct stat st; + char *fileLocation = NULL; + char *fileName; + int length = 0; + int nIndex = 0; + char *strTemp = NULL; + struct stat dirStat; + int j; + int k = 0; + int i = 0; + + if (strchr(strFilePath, '/') != NULL) { + fileName = strrchr(strFilePath, '\\'); + if (fileName == NULL) { + fileName = strrchr(strFilePath, '/'); + } + + if (fileName != NULL) { + if (!strcmp(fileName, "/")) { + goto exit_status; + } + + while (strFilePath[nIndex] != '\0') { + if ('\\' == strFilePath[nIndex] && '\\' == strFilePath[nIndex + 1]) { + goto exit_status; + } + nIndex++; + } + + length = (int)strlen(strFilePath) - (int)strlen(fileName); + + if (fileName == strFilePath) { + length = 1; + } + + if ((fileLocation = (char *)malloc(length + 1)) == NULL) { + goto exit_status; + } + strncpy(fileLocation, strFilePath, length); + fileLocation[length] = '\0'; + + while (fileLocation[k] != '\0') { + if (fileLocation[k] == '\\') { + fileLocation[k] = '/'; + } + k++; + } + + length = (int)strlen(fileLocation); + + if (':' == fileLocation[length - 1]) { + if ((strTemp = (char *)malloc(length + 2)) == NULL) { + goto exit_status; + } + strcpy(strTemp, fileLocation); + strcat(strTemp, "/"); + free(fileLocation); + + length = (int)strlen(strTemp); + if ((fileLocation = (char *)malloc(length + 1)) == NULL) { + goto exit_status; + } + + memcpy(fileLocation, strTemp, length + 1); + free(strTemp); + } + + if (stat(fileLocation, &st) != 0) { + free(fileLocation); + goto exit_status; + } + free(fileLocation); + } else { + goto exit_status; + } + } + + nIndex = 0; + for (i = 0; i < (int)strlen(strSN); i++) { + if (strSN[i] != ' ' && strSN[i] != '\n' && strSN[i] != '\t' && strSN[i] != '\r') { + strMainDirName[nIndex++] = strSN[i]; + } + } + strMainDirName[nIndex] = '\0'; + + j = 1; + while (stat(strMainDirName, &dirStat) == 0) { + strMainDirName[nIndex] = '\0'; + sprintf(strAppend, "-%d", j); + strcat(strMainDirName, strAppend); + j++; + } + + mkdir(strMainDirName, 0777); + + if (strOSDirName != NULL) { + sprintf(strOSDirName, "%s/%s", strMainDirName, "OS"); + mkdir(strOSDirName, 0777); + + } + if (strCtrlDirName != NULL) { + sprintf(strCtrlDirName, "%s/%s", strMainDirName, "Controller"); + mkdir(strCtrlDirName, 0777); + + } + +exit_status: + return err; } static int GetLogPageSize(int nFD, unsigned char ucLogID, int *nLogSize) { - int err = 0; - struct nvme_admin_cmd cmd; - unsigned int uiXferDwords = 0; - unsigned char pTmpBuf[CommonChunkSize] = { 0 }; - LogPageHeader_t *pLogHeader = NULL; - - if (ucLogID == 0xC1 || ucLogID == 0xC2 || ucLogID == 0xC4) { - cmd.opcode = 0x02; - cmd.cdw10 = ucLogID; - uiXferDwords = (unsigned int)(CommonChunkSize / 4); - cmd.nsid = 0xFFFFFFFF; - cmd.cdw10 |= ((uiXferDwords - 1) & 0x0000FFFF) << 16; - cmd.data_len = CommonChunkSize; - cmd.addr = (__u64) (uintptr_t) & pTmpBuf; - err = nvme_submit_passthru(nFD, NVME_IOCTL_ADMIN_CMD, &cmd); - if (err == 0) { - pLogHeader = (LogPageHeader_t *) pTmpBuf; - LogPageHeader_t *pLogHeader1 = (LogPageHeader_t *) pLogHeader; - *nLogSize = (int)(pLogHeader1->numDwordsInEntireLogPage) * 4; - } else { - printf ("Getting size of log page : 0x%X failed with %d\n", ucLogID, err); - *nLogSize = 0; - } - } - return err; + int err = 0; + unsigned char pTmpBuf[CommonChunkSize] = { 0 }; + LogPageHeader_t *pLogHeader = NULL; + + if (ucLogID == 0xC1 || ucLogID == 0xC2 || ucLogID == 0xC4) { + err = nvme_get_log(nFD, NVME_NSID_ALL, ucLogID, false, CommonChunkSize, pTmpBuf); + if (err == 0) { + pLogHeader = (LogPageHeader_t *) pTmpBuf; + LogPageHeader_t *pLogHeader1 = (LogPageHeader_t *) pLogHeader; + *nLogSize = (int)(pLogHeader1->numDwordsInEntireLogPage) * 4; + if (pLogHeader1->logPageHeaderFormatVersion == 0) { + printf ("Unsupported log page format version %d of log page : 0x%X\n", ucLogID, err); + *nLogSize = 0; + err = -1; + } + } else { + printf ("Getting size of log page : 0x%X failed with %d\n", ucLogID, err); + *nLogSize = 0; + } + } + return err; } static int NVMEGetLogPage(int nFD, unsigned char ucLogID, unsigned char *pBuffer, int nBuffSize) { - int err = 0; - struct nvme_admin_cmd cmd = { 0 }; - unsigned int uiNumDwords = (unsigned int)nBuffSize / sizeof(unsigned int); - unsigned int uiMaxChunk = uiNumDwords; - unsigned int uiNumChunks = 1; - unsigned int uiXferDwords = 0; - unsigned long long ullBytesRead = 0; - unsigned char *pTempPtr = pBuffer; - unsigned char ucOpCode = 0x02; - - if (ullBytesRead == 0 && (ucLogID == 0xE6 || ucLogID == 0xE7)) { - uiMaxChunk = 4096; - } else if (uiMaxChunk > 16 * 1024) { - uiMaxChunk = 16 * 1024; - } - - uiNumChunks = uiNumDwords / uiMaxChunk; - if (uiNumDwords % uiMaxChunk > 0) { - uiNumChunks += 1; - } - - for (unsigned int i = 0; i < uiNumChunks; i++) { - memset(&cmd, 0, sizeof(cmd)); - uiXferDwords = uiMaxChunk; - if (i == uiNumChunks - 1 && uiNumDwords % uiMaxChunk > 0) { - uiXferDwords = uiNumDwords % uiMaxChunk; - } - - cmd.opcode = ucOpCode; - cmd.cdw10 |= ucLogID; - cmd.cdw10 |= ((uiXferDwords - 1) & 0x0000FFFF) << 16; - - if (ucLogID == 0x7) { - cmd.cdw10 |= 0x80; - } - if (ullBytesRead == 0 && (ucLogID == 0xE6 || ucLogID == 0xE7)) { - cmd.cdw11 = 1; - } - if (ullBytesRead > 0 && !(ucLogID == 0xE6 || ucLogID == 0xE7)) { - unsigned long long ullOffset = ullBytesRead; - cmd.cdw12 = ullOffset & 0xFFFFFFFF; - cmd.cdw13 = (ullOffset >> 32) & 0xFFFFFFFF; - } - - cmd.addr = (__u64) (uintptr_t) pTempPtr; - cmd.nsid = 0xFFFFFFFF; - cmd.data_len = uiXferDwords * 4; - err = nvme_submit_passthru(nFD, NVME_IOCTL_ADMIN_CMD, &cmd); - ullBytesRead += uiXferDwords * 4; - pTempPtr = pBuffer + ullBytesRead; - } - - return err; + int err = 0; + struct nvme_admin_cmd cmd = { 0 }; + unsigned int uiNumDwords = (unsigned int)nBuffSize / sizeof(unsigned int); + unsigned int uiMaxChunk = uiNumDwords; + unsigned int uiNumChunks = 1; + unsigned int uiXferDwords = 0; + unsigned long long ullBytesRead = 0; + unsigned char *pTempPtr = pBuffer; + unsigned char ucOpCode = 0x02; + + if (ullBytesRead == 0 && (ucLogID == 0xE6 || ucLogID == 0xE7)) { + uiMaxChunk = 4096; + } else if (uiMaxChunk > 16 * 1024) { + uiMaxChunk = 16 * 1024; + } + + uiNumChunks = uiNumDwords / uiMaxChunk; + if (uiNumDwords % uiMaxChunk > 0) { + uiNumChunks += 1; + } + + for (unsigned int i = 0; i < uiNumChunks; i++) { + memset(&cmd, 0, sizeof(cmd)); + uiXferDwords = uiMaxChunk; + if (i == uiNumChunks - 1 && uiNumDwords % uiMaxChunk > 0) { + uiXferDwords = uiNumDwords % uiMaxChunk; + } + + cmd.opcode = ucOpCode; + cmd.cdw10 |= ucLogID; + cmd.cdw10 |= ((uiXferDwords - 1) & 0x0000FFFF) << 16; + + if (ucLogID == 0x7) { + cmd.cdw10 |= 0x80; + } + if (ullBytesRead == 0 && (ucLogID == 0xE6 || ucLogID == 0xE7)) { + cmd.cdw11 = 1; + } + if (ullBytesRead > 0 && !(ucLogID == 0xE6 || ucLogID == 0xE7)) { + unsigned long long ullOffset = ullBytesRead; + cmd.cdw12 = ullOffset & 0xFFFFFFFF; + cmd.cdw13 = (ullOffset >> 32) & 0xFFFFFFFF; + } + + cmd.addr = (__u64) (uintptr_t) pTempPtr; + cmd.nsid = 0xFFFFFFFF; + cmd.data_len = uiXferDwords * 4; + err = nvme_submit_passthru(nFD, NVME_IOCTL_ADMIN_CMD, &cmd); + ullBytesRead += uiXferDwords * 4; + pTempPtr = pBuffer + ullBytesRead; + } + + return err; } static int NVMEResetLog(int nFD, unsigned char ucLogID, int nBufferSize, - long long llMaxSize) + long long llMaxSize) { - unsigned int *pBuffer = NULL; - int err = 0; + unsigned int *pBuffer = NULL; + int err = 0; - if ((pBuffer = (unsigned int *)calloc(1, nBufferSize)) == NULL) - return err; + if ((pBuffer = (unsigned int *)calloc(1, nBufferSize)) == NULL) + return err; - while (err == 0 && llMaxSize > 0) { - err = NVMEGetLogPage(nFD, ucLogID, (unsigned char *)pBuffer, nBufferSize); - if (err) - return err; + while (err == 0 && llMaxSize > 0) { + err = NVMEGetLogPage(nFD, ucLogID, (unsigned char *)pBuffer, nBufferSize); + if (err) + return err; - if (pBuffer[0] == 0xdeadbeef) - break; + if (pBuffer[0] == 0xdeadbeef) + break; - llMaxSize = llMaxSize - nBufferSize; - } + llMaxSize = llMaxSize - nBufferSize; + } - free(pBuffer); - return err; + free(pBuffer); + return err; } static int GetCommonLogPage(int nFD, unsigned char ucLogID, unsigned char **pBuffer, int nBuffSize) { - struct nvme_admin_cmd cmd; - int err = 0; - unsigned char pTmpBuf[CommonChunkSize] = { 0 }; - unsigned int uiMaxChunk = 0; - unsigned int uiXferDwords = 0; - int nBytesRead = 0; - unsigned char *pTempPtr = NULL; - - uiMaxChunk = CommonChunkSize / 4; - pTempPtr = (unsigned char *)malloc(nBuffSize); - if (!pTempPtr) { - goto exit_status; - } - memset(pTempPtr, 0, nBuffSize); - - while (nBytesRead < nBuffSize) { - int nBytesRemaining = nBuffSize - nBytesRead; - - memset(pTmpBuf, 0, CommonChunkSize); - - uiXferDwords = uiMaxChunk; - memset(&cmd, 0, sizeof(cmd)); - cmd.opcode = 0x02; - cmd.cdw10 |= ucLogID; - cmd.cdw10 |= ((uiXferDwords - 1) & 0x0000FFFF) << 16; - - if (nBytesRead > 0) { - unsigned long long ullOffset = (unsigned long long)nBytesRead; - cmd.cdw12 = ullOffset & 0xFFFFFFFF; - cmd.cdw13 = (ullOffset >> 32) & 0xFFFFFFFF; - } - cmd.nsid = 0xFFFFFFFF; - cmd.data_len = uiXferDwords * 4; - cmd.addr = (__u64) (uintptr_t) pTmpBuf; - - err = nvme_submit_passthru(nFD, NVME_IOCTL_ADMIN_CMD, &cmd); - - if (nBytesRemaining >= (int)(uiMaxChunk * 4)) { - memcpy(&pTempPtr[nBytesRead], pTmpBuf, uiMaxChunk * 4); - } else { - memcpy(&pTempPtr[nBytesRead], pTmpBuf, nBytesRemaining); - } - - nBytesRead += (int)uiXferDwords *4; - } - *pBuffer = pTempPtr; - - exit_status: - return err; + unsigned char *pTempPtr = NULL; + int err = 0; + pTempPtr = (unsigned char *)malloc(nBuffSize); + if (!pTempPtr) { + goto exit_status; + } + memset(pTempPtr, 0, nBuffSize); + err = nvme_get_log(nFD, NVME_NSID_ALL, ucLogID, false, nBuffSize, pTempPtr); + *pBuffer = pTempPtr; + +exit_status: + return err; } /* * Plugin Commands */ - -static int micron_selective_download(int argc, char **argv, struct command *cmd, struct plugin *plugin) +static int micron_parse_options(int argc, char **argv, const char *desc, + const struct argconfig_commandline_options *opts, eDriveModel *modelp) { - const char *desc = - "This performs a selective firmware download, which allows the user to " - "select which firmware binary to update for 9200 devices. This requires a power cycle once the " - "update completes. The options available are: \n\n" - "OOB - This updates the OOB and main firmware\n" - "EEP - This updates the eeprom and main firmware\n" - "ALL - This updates the eeprom, OOB, and main firmware"; - const char *fw = "firmware file (required)"; - const char *select = "FW Select (e.g., --select=ALL)"; - int xfer = 4096; - void *fw_buf; - int fd, selectNo, fw_fd, fw_size, err, offset = 0; - struct stat sb; - - struct config { - char *fw; - char *select; - }; - - struct config cfg = { - .fw = "", - .select = "\0", - }; - - OPT_ARGS(opts) = { - OPT_STRING("fw", 'f', "FILE", &cfg.fw, fw), - OPT_STRING("select", 's', "flag", &cfg.select, select), - OPT_END() - }; - - fd = parse_and_open(argc, argv, desc, opts); - - if (fd < 0) - return fd; - - if (strlen(cfg.select) != 3) { - fprintf(stderr, "Invalid select flag\n"); - err = EINVAL; - goto out; - } - - for (int i = 0; i < 3; i++) { - cfg.select[i] = toupper(cfg.select[i]); - } - - if (strncmp(cfg.select, "OOB", 3) == 0) { - selectNo = 18; - } else if (strncmp(cfg.select, "EEP", 3) == 0) { - selectNo = 10; - } else if (strncmp(cfg.select, "ALL", 3) == 0) { - selectNo = 26; - } else { - fprintf(stderr, "Invalid select flag\n"); - err = EINVAL; - goto out; - } - - fw_fd = open(cfg.fw, O_RDONLY); - if (fw_fd < 0) { - fprintf(stderr, "no firmware file provided\n"); - err = EINVAL; - goto out; - } - - err = fstat(fw_fd, &sb); - if (err < 0) { - perror("fstat"); - err = errno; - } + int idx = 0; + int fd = parse_and_open(argc, argv, desc, opts); - fw_size = sb.st_size; - if (fw_size & 0x3) { - fprintf(stderr, "Invalid size:%d for f/w image\n", fw_size); - err = EINVAL; - goto out; - } - - if (posix_memalign(&fw_buf, getpagesize(), fw_size)) { - fprintf(stderr, "No memory for f/w size:%d\n", fw_size); - err = ENOMEM; - goto out; - } + if (fd < 0) { + perror("open"); + return -1; + } - if (read(fw_fd, fw_buf, fw_size) != ((ssize_t) (fw_size))) - return EIO; - - while (fw_size > 0) { - xfer = min(xfer, fw_size); - - err = nvme_fw_download(fd, offset, xfer, fw_buf); - if (err < 0) { - perror("fw-download"); - goto out; - } else if (err != 0) { - fprintf(stderr, "NVME Admin command error:%s(%x)\n", - nvme_status_to_string(err), err); - goto out; - } - fw_buf += xfer; - fw_size -= xfer; - offset += xfer; - } + sscanf(argv[optind], "/dev/nvme%d", &idx); + *modelp = GetDriveModel(idx); + return fd; +} - err = micron_fw_commit(fd, selectNo); +static int micron_fw_commit(int fd, int select) +{ + struct nvme_admin_cmd cmd = { + .opcode = nvme_admin_activate_fw, + .cdw10 = 8, + .cdw12 = select, + }; + return ioctl(fd, NVME_IOCTL_ADMIN_CMD, &cmd); +} - if (err == 0x10B || err == 0x20B) { - err = 0; - fprintf(stderr, - "Update successful! Please power cycle for changes to take effect\n"); - } +static int micron_selective_download(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + const char *desc = + "This performs a selective firmware download, which allows the user to " + "select which firmware binary to update for 9200 devices. This requires a power cycle once the " + "update completes. The options available are: \n\n" + "OOB - This updates the OOB and main firmware\n" + "EEP - This updates the eeprom and main firmware\n" + "ALL - This updates the eeprom, OOB, and main firmware"; + const char *fw = "firmware file (required)"; + const char *select = "FW Select (e.g., --select=ALL)"; + int xfer = 4096; + void *fw_buf; + int fd, selectNo, fw_fd, fw_size, err, offset = 0; + struct stat sb; + + struct config { + char *fw; + char *select; + }; + + struct config cfg = { + .fw = "", + .select = "\0", + }; + + OPT_ARGS(opts) = { + OPT_STRING("fw", 'f', "FILE", &cfg.fw, fw), + OPT_STRING("select", 's', "flag", &cfg.select, select), + OPT_END() + }; + + fd = parse_and_open(argc, argv, desc, opts); + + if (fd < 0) + return fd; + + if (strlen(cfg.select) != 3) { + fprintf(stderr, "Invalid select flag\n"); + err = EINVAL; + goto out; + } + + for (int i = 0; i < 3; i++) { + cfg.select[i] = toupper(cfg.select[i]); + } + + if (strncmp(cfg.select, "OOB", 3) == 0) { + selectNo = 18; + } else if (strncmp(cfg.select, "EEP", 3) == 0) { + selectNo = 10; + } else if (strncmp(cfg.select, "ALL", 3) == 0) { + selectNo = 26; + } else { + fprintf(stderr, "Invalid select flag\n"); + err = EINVAL; + goto out; + } + + fw_fd = open(cfg.fw, O_RDONLY); + if (fw_fd < 0) { + fprintf(stderr, "no firmware file provided\n"); + err = EINVAL; + goto out; + } + + err = fstat(fw_fd, &sb); + if (err < 0) { + perror("fstat"); + err = errno; + } + + fw_size = sb.st_size; + if (fw_size & 0x3) { + fprintf(stderr, "Invalid size:%d for f/w image\n", fw_size); + err = EINVAL; + goto out; + } + + if (posix_memalign(&fw_buf, getpagesize(), fw_size)) { + fprintf(stderr, "No memory for f/w size:%d\n", fw_size); + err = ENOMEM; + goto out; + } + + if (read(fw_fd, fw_buf, fw_size) != ((ssize_t) (fw_size))) + return EIO; + + while (fw_size > 0) { + xfer = min(xfer, fw_size); + + err = nvme_fw_download(fd, offset, xfer, fw_buf); + if (err < 0) { + perror("fw-download"); + goto out; + } else if (err != 0) { + fprintf(stderr, "NVME Admin command error:%s(%x)\n", + nvme_status_to_string(err), err); + goto out; + } + fw_buf += xfer; + fw_size -= xfer; + offset += xfer; + } + + err = micron_fw_commit(fd, selectNo); + + if (err == 0x10B || err == 0x20B) { + err = 0; + fprintf(stderr, + "Update successful! Please power cycle for changes to take effect\n"); + } + +out: + return err; +} - out: - return err; +static int micron_smbus_option(int argc, char **argv, struct command *cmd, struct plugin *plugin) +{ + __u32 result = 0; + __u32 cdw10 = 0; + __u32 cdw11 = 0; + const char *desc = "Enable/Disable/Get status of SMBUS option on controller"; + const char *option = "enable or disable or status"; + const char *value = "1 - hottest component temperature, 0 - composite temperature (default) for enable option, 0 (current), 1 (default), 2 (saved) for status options"; + const char *save = "1 - persistent, 0 - non-persistent (default)"; + int err = 0; + int fd = 0; + int fid = 0xD5; + eDriveModel model = UNKNOWN_MODEL; + + struct { + char *option; + int value; + int save; + int status; + } opt = { + .option = "disable", + .value = 0, + .save = 0, + .status = 0, + }; + + OPT_ARGS(opts) = { + OPT_STRING("option", 'o', "option", &opt.option, option), + OPT_UINT("value", 'v', &opt.value, value), + OPT_UINT("save", 's', &opt.save, save), + OPT_END() + }; + + if ((fd = micron_parse_options(argc, argv, desc, opts, &model)) < 0) + return err; + + if (model != M5407 && model != M5411) { + printf ("This option is not supported for specified drive\n"); + close(fd); + return err; + } + + if (!strcmp(opt.option, "enable")) { + cdw11 = opt.value << 1 | 1; + err = nvme_set_feature(fd, 1, fid, cdw11, 0, opt.save, 0, 0, &result); + if (err == 0) { + printf("successfully enabled SMBus on drive\n"); + } else { + printf("Failed to enabled SMBus on drive\n"); + } + } + else if (!strcmp(opt.option, "status")) { + cdw10 = opt.value; + err = nvme_get_feature(fd, 1, fid, cdw10, 0, 0, 0, &result); + if (err == 0) { + printf("SMBus status on the drive: %s (returns %s temperature) \n", + (result & 1) ? "enabled" : "disabled", + (result & 2) ? "hottest component" : "composite"); + } else { + printf("Failed to retrieve SMBus status on the drive\n"); + } + } + else if (!strcmp(opt.option, "disable")) { + cdw11 = opt.value << 1 | 0; + err = nvme_set_feature(fd, 1, fid, cdw11, 0, opt.save, 0, 0, &result); + if (err == 0) { + printf("Successfully disabled SMBus on drive\n"); + } else { + printf("Failed to disable SMBus on drive\n"); + } + } else { + printf("invalid option %s, valid values are enable, disable or status\n", opt.option); + close(fd); + return -1; + } + + close(fd); + return err; } static int micron_temp_stats(int argc, char **argv, struct command *cmd, struct plugin *plugin) { - struct nvme_smart_log smart_log; - unsigned int temperature = 0, i = 0, fd = 0, err = 0; - unsigned int tempSensors[SensorCount] = { 0 }; - const char *desc = "Retrieve Micron temperature info for the given device "; - - OPT_ARGS(opts) = { - OPT_END() - }; - - fd = parse_and_open(argc, argv, desc, opts); - if (fd < 0) { - printf("\nDevice not found \n");; - return -1; - } - - err = nvme_smart_log(fd, 0xffffffff, &smart_log); - if (!err) { - printf("Micron temperature information:\n"); - temperature = ((smart_log.temperature[1] << 8) | smart_log.temperature[0]); - temperature = temperature ? temperature - 273 : 0; - for (i = 0; i < SensorCount; i++) { - tempSensors[i] = le16_to_cpu(smart_log.temp_sensor[i]); - tempSensors[i] = tempSensors[i] ? tempSensors[i] - 273 : 0; - } - printf("%-10s : %u C\n", "Current Composite Temperature", temperature); - for (i = 0; i < SensorCount; i++) { - printf("%-10s%d : %u C\n", "Temperature Sensor #", i + 1, tempSensors[i]); - } - } - return err; + struct nvme_smart_log smart_log; + unsigned int temperature = 0, i = 0, err = 0; + unsigned int tempSensors[SensorCount] = { 0 }; + const char *desc = "Retrieve Micron temperature info for the given device "; + const char *fmt = "output format normal|json"; + struct format { + char *fmt; + }; + struct format cfg = { + .fmt = "normal", + }; + bool is_json = false; + struct json_object *root; + struct json_array *logPages; + int fd; + + OPT_ARGS(opts) = { + OPT_FMT("format", 'f', &cfg.fmt, fmt), + OPT_END() + }; + + fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) { + printf("\nDevice not found \n");; + return -1; + } + + if (strcmp(cfg.fmt, "json") == 0) + is_json = true; + + err = nvme_smart_log(fd, 0xffffffff, &smart_log); + if (!err) { + temperature = ((smart_log.temperature[1] << 8) | smart_log.temperature[0]); + temperature = temperature ? temperature - 273 : 0; + for (i = 0; i < SensorCount; i++) { + tempSensors[i] = le16_to_cpu(smart_log.temp_sensor[i]); + tempSensors[i] = tempSensors[i] ? tempSensors[i] - 273 : 0; + } + if (is_json) { + struct json_object *stats = json_create_object(); + char tempstr[64] = { 0 }; + root = json_create_object(); + logPages = json_create_array(); + json_object_add_value_array(root, "Micron temperature information", logPages); + sprintf(tempstr, "%u C", temperature); + json_object_add_value_string(stats, "Current Composite Temperature", tempstr); + for (i = 0; i < SensorCount; i++) { + char sensor_str[256] = { 0 }; + char datastr[64] = { 0 }; + sprintf(sensor_str, "Temperature Sensor #%d", (i + 1)); + sprintf(datastr, "%u C", tempSensors[i]); + json_object_add_value_string(stats, sensor_str, datastr); + } + json_array_add_value_object(logPages, stats); + json_print_object(root, NULL); + printf("\n"); + json_free_object(root); + } else { + printf("Micron temperature information:\n"); + printf("%-10s : %u C\n", "Current Composite Temperature", temperature); + for (i = 0; i < SensorCount; i++) { + printf("%-10s%d : %u C\n", "Temperature Sensor #", i + 1, tempSensors[i]); + } + } + } + return err; } static int micron_pcie_stats(int argc, char **argv, struct command *cmd, struct plugin *plugin) { - int err = 0, bus = 0, domain = 0, device = 0, function = 0; - char strTempFile[1024], strTempFile2[1024], command[1024]; - char *businfo = NULL; - char *devicename = NULL; - char tdevice[NAME_MAX] = { 0 }; - ssize_t sLinkSize = 0; - FILE *fp; - char correctable[8] = { 0 }; - char uncorrectable[8] = { 0 }; - char *res; - - if (argc != 2) { - printf("vs-pcie-stats: Invalid argument\n"); - printf("Usage: nvme micron vs-pcie-stats \n\n"); - goto out; - } - if (strstr(argv[optind], "/dev/nvme") && strstr(argv[optind], "n1")) { - devicename = strrchr(argv[optind], '/'); - } else if (strstr(argv[optind], "/dev/nvme")) { - devicename = strrchr(argv[optind], '/'); - sprintf(tdevice, "%s%s", devicename, "n1"); - devicename = tdevice; - } else { - printf("Invalid device specified!\n"); - goto out; - } - sprintf(strTempFile, "/sys/block/%s/device", devicename); - sLinkSize = readlink(strTempFile, strTempFile2, 1024); - if (sLinkSize < 0) { - printf("Unable to read device\n"); - goto out; - } - if (strstr(strTempFile2, "../../nvme")) { - sprintf(strTempFile, "/sys/block/%s/device/device", devicename); - sLinkSize = readlink(strTempFile, strTempFile2, 1024); - if (sLinkSize < 0) { - printf("Unable to read device\n"); - goto out; - } + int i, fd, err = 0, bus = 0, domain = 0, device = 0, function = 0, ctrlIdx; + char strTempFile[1024], strTempFile2[1024], command[1024]; + char *businfo = NULL; + char *devicename = NULL; + char tdevice[NAME_MAX] = { 0 }; + ssize_t sLinkSize = 0; + FILE *fp; + char correctable[8] = { 0 }; + char uncorrectable[8] = { 0 }; + eDriveModel eModel = UNKNOWN_MODEL; + char *res; + bool is_json = false; + struct format { + char *fmt; + }; + const char *desc = "Retrieve PCIe event counters"; + const char *fmt = "output format normal|json"; + struct format cfg = { + .fmt = "normal", + }; + struct { + char *err; + int bit; + int val; + } pcie_correctable_errors[] = { + { "Unsupported Request Error Status (URES)", 20}, + { "ECRC Error Status (ECRCES)", 19}, + { "Malformed TLP Status (MTS)", 18}, + { "Receiver Overflow Status (ROS)", 17}, + { "Unexpected Completion Status (UCS)", 16}, + { "Completer Abort Status (CAS)", 15}, + { "Completion Timeout Stats (CTS)", 14}, + { "Flow Control Protocol Error Status (FCPES)", 13}, + { "Poisoned TLP Status (PTS)", 12}, + { "Data Link Protocol Error Status (DLPES)", 4}, + }, + pcie_uncorrectable_errors[] = { + { "Advisory Non-Fatal Error Status (ANFES)", 13}, + { "Replay Timer Timeout Status (RTS)", 12}, + { "REPLY NUM Rollover Status (RRS)", 8}, + { "Bad DLLP Status (BDS)", 7}, + { "Bad TLP Status (BTS)", 6}, + { "Receiver Error Status (RES)", 0}, + }; + + __u32 correctable_errors; + __u32 uncorrectable_errors; + + OPT_ARGS(opts) = { + OPT_FMT("format", 'f', &cfg.fmt, fmt), + OPT_END() + }; + + fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) { + printf("\nDevice not found \n");; + return -1; + } + + /* pull log details based on the model name */ + sscanf(argv[optind], "/dev/nvme%d", &ctrlIdx); + if ((eModel = GetDriveModel(ctrlIdx)) == UNKNOWN_MODEL) { + printf ("Unsupported drive model for vs-internal-log collection\n"); + close(fd); + goto out; + } + + if (strcmp(cfg.fmt, "json") == 0) + is_json = true; + + if (strstr(argv[optind], "/dev/nvme") && strstr(argv[optind], "n1")) { + devicename = strrchr(argv[optind], '/'); + } else if (strstr(argv[optind], "/dev/nvme")) { + devicename = strrchr(argv[optind], '/'); + sprintf(tdevice, "%s%s", devicename, "n1"); + devicename = tdevice; + } else { + printf("Invalid device specified!\n"); + goto out; + } + sprintf(strTempFile, "/sys/block/%s/device", devicename); + sLinkSize = readlink(strTempFile, strTempFile2, 1024); + if (sLinkSize < 0) { + printf("Failed to read device\n"); + goto out; + } + if (strstr(strTempFile2, "../../nvme")) { + sprintf(strTempFile, "/sys/block/%s/device/device", devicename); + sLinkSize = readlink(strTempFile, strTempFile2, 1024); + if (sLinkSize < 0) { + printf("Failed to read device\n"); + goto out; + } + } + businfo = strrchr(strTempFile2, '/'); + sscanf(businfo, "/%x:%x:%x.%x", &domain, &bus, &device, &function); + sprintf(command, "setpci -s %x:%x.%x ECAP_AER+10.L", bus, device, + function); + fp = popen(command, "r"); + if (fp == NULL) { + printf("Failed to retrieve error count\n"); + goto out; + } + res = fgets(correctable, sizeof(correctable), fp); + if (res == NULL) { + printf("Failed to retrieve error count\n"); + goto out; + } + pclose(fp); + + sprintf(command, "setpci -s %x:%x.%x ECAP_AER+0x4.L", bus, device, + function); + fp = popen(command, "r"); + if (fp == NULL) { + printf("Failed to retrieve error count\n"); + goto out; + } + res = fgets(uncorrectable, sizeof(uncorrectable), fp); + if (res == NULL) { + printf("Failed to retrieve error count\n"); + goto out; + } + pclose(fp); + + correctable_errors = (__u32)strtol(correctable, NULL, 16); + uncorrectable_errors = (__u32)strtol(uncorrectable, NULL, 16); + + if (is_json) { + + struct json_object *root = json_create_object(); + struct json_array *pcieErrors = json_create_array(); + struct json_object *stats = json_create_object(); + + json_object_add_value_array(root, "PCIE Stats", pcieErrors); + for (i = 0; i < sizeof(pcie_correctable_errors) / sizeof(pcie_correctable_errors[0]); i++) { + json_object_add_value_int(stats, pcie_correctable_errors[i].err, + ((correctable_errors >> pcie_correctable_errors[i].bit) & 1)); } - businfo = strrchr(strTempFile2, '/'); - sscanf(businfo, "/%x:%x:%x.%x", &domain, &bus, &device, &function); - sprintf(command, "setpci -s %x:%x.%x ECAP_AER+10.L", bus, device, - function); - fp = popen(command, "r"); - if (fp == NULL) { - printf("Unable to retrieve error count\n"); - goto out; + for (i = 0; i < sizeof(pcie_uncorrectable_errors) / sizeof(pcie_uncorrectable_errors[0]); i++) { + json_object_add_value_int(stats, pcie_uncorrectable_errors[i].err, + ((uncorrectable_errors >> pcie_uncorrectable_errors[i].bit) & 1)); } - res = fgets(correctable, sizeof(correctable), fp); - if (res == NULL) { - printf("Unable to retrieve error count\n"); - goto out; + json_array_add_value_object(pcieErrors, stats); + json_print_object(root, NULL); + printf("\n"); + json_free_object(root); + } else if (eModel == M5407) { + for (i = 0; i < sizeof(pcie_correctable_errors) / sizeof(pcie_correctable_errors[0]); i++) { + printf("%-40s : %-1d\n", pcie_correctable_errors[i].err, + ((correctable_errors >> pcie_correctable_errors[i].bit) & 1)); } - pclose(fp); - - sprintf(command, "setpci -s %x:%x.%x ECAP_AER+0x4.L", bus, device, - function); - fp = popen(command, "r"); - if (fp == NULL) { - printf("Unable to retrieve error count\n"); - goto out; + for (i = 0; i < sizeof(pcie_uncorrectable_errors) / sizeof(pcie_uncorrectable_errors[0]); i++) { + printf("%-40s : %-1d\n", pcie_uncorrectable_errors[i].err, + ((uncorrectable_errors >> pcie_uncorrectable_errors[i].bit) & 1)); } - res = fgets(uncorrectable, sizeof(uncorrectable), fp); - if (res == NULL) { - printf("Unable to retrieve error count\n"); - goto out; - } - pclose(fp); - printf("PCIE Stats:\n"); - printf("Device correctable errors detected: %s\n", correctable); - printf("Device uncorrectable errors detected: %s\n", uncorrectable); - - out: - return err; + } else { + printf("PCIE Stats:\n"); + printf("Device correctable errors detected: %s\n", correctable); + printf("Device uncorrectable errors detected: %s\n", uncorrectable); + } + +out: + return err; } static int micron_clear_pcie_correctable_errors(int argc, char **argv, - struct command *cmd, - struct plugin *plugin) + struct command *cmd, + struct plugin *plugin) { - int err = 0, bus = 0, domain = 0, device = 0, function = 0; - char strTempFile[1024], strTempFile2[1024], command[1024]; - char *businfo = NULL; - char *devicename = NULL; - char tdevice[PATH_MAX] = { 0 }; - ssize_t sLinkSize = 0; - FILE *fp; - char correctable[8] = { 0 }; - char *res; - - if (argc != 2) { - printf("clear-pcie-correctable-errors: Invalid argument\n"); - printf ("Usage: nvme micron clear-pcie-correctable-errors \n\n"); - goto out; - } - if (strstr(argv[optind], "/dev/nvme") && strstr(argv[optind], "n1")) { - devicename = strrchr(argv[optind], '/'); - } else if (strstr(argv[optind], "/dev/nvme")) { - devicename = strrchr(argv[optind], '/'); - sprintf(tdevice, "%s%s", devicename, "n1"); - devicename = tdevice; - } else { - printf("Invalid device specified!\n"); - goto out; - } - err = snprintf(strTempFile, sizeof(strTempFile), - "/sys/block/%s/device", devicename); - if (err < 0) - goto out; - sLinkSize = readlink(strTempFile, strTempFile2, 1024); - if (sLinkSize < 0) { - printf("Unable to read device\n"); - goto out; - } - if (strstr(strTempFile2, "../../nvme")) { - err = snprintf(strTempFile, sizeof(strTempFile), - "/sys/block/%s/device/device", devicename); - if (err < 0) - goto out; - sLinkSize = readlink(strTempFile, strTempFile2, 1024); - if (sLinkSize < 0) { - printf("Unable to read device\n"); - goto out; - } - } - businfo = strrchr(strTempFile2, '/'); - sscanf(businfo, "/%x:%x:%x.%x", &domain, &bus, &device, &function); - sprintf(command, "setpci -s %x:%x.%x ECAP_AER+0x10.L=0xffffffff", bus, - device, function); - - fp = popen(command, "r"); - if (fp == NULL) { - printf("Unable to clear error count\n"); - goto out; - } - pclose(fp); - - sprintf(command, "setpci -s %x:%x.%x ECAP_AER+0x10.L", bus, device, - function); - fp = popen(command, "r"); - if (fp == NULL) { - printf("Unable to retrieve error count\n"); - goto out; - } - res = fgets(correctable, sizeof(correctable), fp); - if (res == NULL) { - printf("Unable to retrieve error count\n"); - goto out; - } - pclose(fp); - printf("Device correctable errors cleared!\n"); - printf("Device correctable errors detected: %s\n", correctable); + int err = 0, bus = 0, domain = 0, device = 0, function = 0; + char strTempFile[1024], strTempFile2[1024], command[1024]; + char *businfo = NULL; + char *devicename = NULL; + char tdevice[PATH_MAX] = { 0 }; + ssize_t sLinkSize = 0; + FILE *fp; + char correctable[8] = { 0 }; + char *res; + + if (argc != 2) { + printf("clear-pcie-correctable-errors: Invalid argument\n"); + printf ("Usage: nvme micron clear-pcie-correctable-errors \n\n"); + goto out; + } + if (strstr(argv[optind], "/dev/nvme") && strstr(argv[optind], "n1")) { + devicename = strrchr(argv[optind], '/'); + } else if (strstr(argv[optind], "/dev/nvme")) { + devicename = strrchr(argv[optind], '/'); + sprintf(tdevice, "%s%s", devicename, "n1"); + devicename = tdevice; + } else { + printf("Invalid device specified!\n"); + goto out; + } + err = snprintf(strTempFile, sizeof(strTempFile), + "/sys/block/%s/device", devicename); + if (err < 0) + goto out; + sLinkSize = readlink(strTempFile, strTempFile2, 1024); + if (sLinkSize < 0) { + printf("Failed to read device\n"); + goto out; + } + if (strstr(strTempFile2, "../../nvme")) { + err = snprintf(strTempFile, sizeof(strTempFile), + "/sys/block/%s/device/device", devicename); + if (err < 0) + goto out; + sLinkSize = readlink(strTempFile, strTempFile2, 1024); + if (sLinkSize < 0) { + printf("Failed to read device\n"); + goto out; + } + } + businfo = strrchr(strTempFile2, '/'); + sscanf(businfo, "/%x:%x:%x.%x", &domain, &bus, &device, &function); + sprintf(command, "setpci -s %x:%x.%x ECAP_AER+0x10.L=0xffffffff", bus, + device, function); + + fp = popen(command, "r"); + if (fp == NULL) { + printf("Failed to clear error count\n"); + goto out; + } + pclose(fp); + + sprintf(command, "setpci -s %x:%x.%x ECAP_AER+0x10.L", bus, device, + function); + fp = popen(command, "r"); + if (fp == NULL) { + printf("Failed to retrieve error count\n"); + goto out; + } + res = fgets(correctable, sizeof(correctable), fp); + if (res == NULL) { + printf("Failed to retrieve error count\n"); + goto out; + } + pclose(fp); + printf("Device correctable errors cleared!\n"); + printf("Device correctable errors detected: %s\n", correctable); + +out: + return err; +} - out: - return err; +static void print_m5407_nand_stats(const unsigned char *buf, bool is_json) +{ + struct logpage_t { + char *field; + int size; + } fb_log_page[] = { + { "Physical Media Units Written - TLC", 16 }, + { "Physical Media Units Written - SLC", 16 }, + { "Bad User NAND Block Count", 8}, + { "XOR Recovery Count", 8}, + { "Uncorrectable Read Error Count", 8}, + { "SSD End to End Corrected Errors", 8}, + { "SSD End to End Detected Counts", 4}, + { "SSD End to End Uncorrected Counts", 4}, + { "System data %% life-used", 1}, + { "Minium User Data Erase Count - TLC", 8}, + { "Maximum User Data Erase Count - TLC", 8}, + { "Minium User Data Erase Count - SLC", 8}, + { "Maximum User Data Erase Count - SLC", 8}, + { "Raw Program Fail Count", 6}, + { "Normalized Program Fail Count", 2}, + { "Raw Erase Fail Count", 6}, + { "Normalized Erase Fail Count", 2}, + { "Pcie Correctable Error Count", 8}, + { "%% Free Blocks (User)", 1}, + { "Security Version Number", 8}, + { "%% Free Blocks (System)", 1}, + { "Dataset Management Commands", 16}, + { "Incomplete TRIM Data", 8}, + { "%% Age of Completed TRIM", 1}, + { "Background Back-Presure Gauge", 1}, + { "Soft ECC Error Count", 8}, + { "Refresh Count", 8}, + { "Raw Bad System NAND Block Count", 6}, + { "Normalized Bad System NAND Block Count", 2}, + { "Endurance Estimate", 16}, + { "Thermal Throttling Status", 1}, + { "Thermal Throttling Count", 1}, + { "Unaligned I/O", 8}, + { "Physical Media Units Read", 16}, + { "Reserved", 279}, + { "Log Page Version", 2} + }; + struct json_object *root; + struct json_array *logPages; + struct json_object *stats; + int field; + int offset = 0; + __u64 lval_lo, lval_hi; + __u32 ival; + __u16 sval; + __u8 cval; + + if (is_json) { + root = json_create_object(); + stats = json_create_object(); + logPages = json_create_array(); + json_object_add_value_array(root, "Extended Smart Log Page : 0xFB", logPages); + } + + for (field = 0; field < sizeof(fb_log_page)/sizeof(fb_log_page[0]); field++) { + char datastr[1024] = { 0 }; + if (fb_log_page[field].size == 16) { + lval_lo = *((__u64 *)(&buf[offset])); + lval_hi = *((__u64 *)(&buf[offset + 8])); + sprintf(datastr, "0x%lx_%lx", le64_to_cpu(lval_hi), le64_to_cpu(lval_lo)); + } else if (fb_log_page[field].size == 8) { + lval_lo = *((__u64 *)(&buf[offset])); + sprintf(datastr, "0x%lx", le64_to_cpu(lval_lo)); + } else if (fb_log_page[field].size == 6) { + ival = *((__u32 *)(&buf[offset])); + sval = *((__u16 *)(&buf[offset + 4])); + lval_lo = (((__u64)sval << 32) | ival); + sprintf(datastr, "0x%lx", le64_to_cpu(lval_lo)); + } else if (fb_log_page[field].size == 4) { + ival = *((__u32 *)(&buf[offset])); + sprintf(datastr, "0x%x", le32_to_cpu(ival)); + } else if (fb_log_page[field].size == 2) { + sval = *((__u16 *)(&buf[offset])); + sprintf(datastr, "0x%x", le16_to_cpu(sval)); + } else if (fb_log_page[field].size == 1) { + cval = buf[offset]; + sprintf(datastr, "0x%x", cval); + } else { + sprintf(datastr, "0"); + } + if (is_json) { + json_object_add_value_string(stats, fb_log_page[field].field, datastr); + json_array_add_value_object(logPages, stats); + } else { + printf("%-40s : %-4s\n", fb_log_page[field].field, datastr); + } + offset += fb_log_page[field].size; + } + + if (is_json) { + json_print_object(root, NULL); + printf("\n"); + json_free_object(root); + } } static int micron_nand_stats(int argc, char **argv, struct command *cmd, struct plugin *plugin) { - const char *desc = "Retrieve Micron NAND stats for the given device "; - unsigned int extSmartLog[64] = { 0 }; - struct nvme_id_ctrl ctrl; - int fd, err; - - OPT_ARGS(opts) = { - OPT_END() - }; - - fd = parse_and_open(argc, argv, desc, opts); - if (fd < 0) { - printf("\nDevice not found \n");; - return -1; - } - - err = nvme_identify_ctrl(fd, &ctrl); - if (err) - goto out; - - err = NVMEGetLogPage(fd, 0xD0, (unsigned char *)extSmartLog, D0_log_size); - if (err) - goto out; - - unsigned long long count = ((unsigned long long)extSmartLog[45] << 32) | extSmartLog[44]; - printf("%-40s : 0x%llx\n", "NAND Writes (Bytes Written)", count); - printf("%-40s : ", "Program Failure Count"); - - unsigned long long count_hi = ((unsigned long long)extSmartLog[39] << 32) | extSmartLog[38]; - unsigned long long count_lo = ((unsigned long long)extSmartLog[37] << 32) | extSmartLog[36]; - if (count_hi != 0) - printf("0x%llx%016llx", count_hi, count_lo); - else - printf("0x%llx\n", count_lo); - - count = ((unsigned long long)extSmartLog[25] << 32) | extSmartLog[24]; - printf("%-40s : 0x%llx\n", "Erase Failures", count); - printf("%-40s : 0x%x\n", "Bad Block Count", extSmartLog[3]); - - count = (unsigned long long)extSmartLog[3] - (count_lo + count); - printf("%-40s : 0x%llx\n", "NAND XOR/RAID Recovery Trigger Events", count); - printf("%-40s : 0x%x\n", "NSZE Change Supported", (ctrl.oacs >> 3) & 0x1); - printf("%-40s : 0x%x\n", "Number of NSZE Modifications", extSmartLog[1]); - out: - close(fd); - return err; + const char *desc = "Retrieve Micron NAND stats for the given device "; + unsigned int extSmartLog[64] = { 0 }; + eDriveModel eModel = UNKNOWN_MODEL; + struct nvme_id_ctrl ctrl; + int fd, err, ctrlIdx; + int log_size = D0_log_size; + unsigned char log_page = 0xD0; + bool is_json = false; + struct format { + char *fmt; + }; + const char *fmt = "output format normal|json"; + struct format cfg = { + .fmt = "normal", + }; + + OPT_ARGS(opts) = { + OPT_FMT("format", 'f', &cfg.fmt, fmt), + OPT_END() + }; + + fd = parse_and_open(argc, argv, desc, opts); + if (fd < 0) { + printf("\nDevice not found \n");; + return -1; + } + + if (strcmp(cfg.fmt, "json") == 0) + is_json = true; + + err = nvme_identify_ctrl(fd, &ctrl); + if (err) + goto out; + + /* pull log details based on the model name */ + sscanf(argv[optind], "/dev/nvme%d", &ctrlIdx); + if ((eModel = GetDriveModel(ctrlIdx)) == UNKNOWN_MODEL) { + printf ("Unsupported drive model for vs-internal-log collection\n"); + close(fd); + goto out; + } + /* should check for firmware version if this log is supported or not */ + if (eModel == M5407) { + log_page = 0xFB; + log_size = FB_log_size; + } + err = nvme_get_log(fd, NVME_NSID_ALL, log_page, false, log_size, extSmartLog); + if (err) { + printf("Unable to retrieve extended smart log for the drive\n"); + goto out; + } + + if (eModel == M5407) { + printf("Print log in json(%d) format\n", is_json); + print_m5407_nand_stats((__u8 *)extSmartLog, is_json); + goto out; + } + + unsigned long long count = ((unsigned long long)extSmartLog[45] << 32) | extSmartLog[44]; + printf("%-40s : 0x%llx\n", "NAND Writes (Bytes Written)", count); + printf("%-40s : ", "Program Failure Count"); + + unsigned long long count_hi = ((unsigned long long)extSmartLog[39] << 32) | extSmartLog[38]; + unsigned long long count_lo = ((unsigned long long)extSmartLog[37] << 32) | extSmartLog[36]; + if (count_hi != 0) + printf("0x%llx%016llx", count_hi, count_lo); + else + printf("0x%llx\n", count_lo); + + count = ((unsigned long long)extSmartLog[25] << 32) | extSmartLog[24]; + printf("%-40s : 0x%llx\n", "Erase Failures", count); + printf("%-40s : 0x%x\n", "Bad Block Count", extSmartLog[3]); + + count = (unsigned long long)extSmartLog[3] - (count_lo + count); + printf("%-40s : 0x%llx\n", "NAND XOR/RAID Recovery Trigger Events", count); + printf("%-40s : 0x%x\n", "NSZE Change Supported", (ctrl.oacs >> 3) & 0x1); + printf("%-40s : 0x%x\n", "Number of NSZE Modifications", extSmartLog[1]); +out: + close(fd); + return err; } -typedef enum { M5410 = 0, M51AX, M51BX, UNKNOWN_MODEL } eDriveModel; -static char *fvendorid1 = "/sys/class/nvme/nvme%d/device/vendor"; -static char *fvendorid2 = "/sys/class/misc/nvme%d/device/vendor"; -static char *fdeviceid1 = "/sys/class/nvme/nvme%d/device/device"; -static char *fdeviceid2 = "/sys/class/misc/nvme%d/device/device"; -static unsigned short vendor_id; -static unsigned short device_id; - -static int ReadSysFile(const char *file, unsigned short *id) +static void GetDriveInfo(const char *strOSDirName, int nFD, + struct nvme_id_ctrl *ctrlp) { - int ret = 0; - char idstr[32] = { '\0' }; - int fd = open(file, O_RDONLY); - - if (fd > 0) { - ret = read(fd, idstr, sizeof(idstr)); - close(fd); - } - - if (fd < 0 || ret < 0) - perror(file); - else - *id = strtol(idstr, NULL, 16); - - return ret; + FILE *fpOutFile = NULL; + char tempFile[256] = { 0 }; + char strBuffer[1024] = { 0 }; + char model[41] = { 0 }; + char serial[21] = { 0 }; + char fwrev[9] = { 0 }; + char *strPDir = strdup(strOSDirName); + char *strDest = dirname(strPDir); + + sprintf(tempFile, "%s/%s", strDest, "drive-info.txt"); + fpOutFile = fopen(tempFile, "w+"); + if (!fpOutFile) { + printf("Failed to create %s\n", tempFile); + free(strPDir); + return; + } + + strncpy(model, ctrlp->mn, 40); + strncpy(serial, ctrlp->sn, 20); + strncpy(fwrev, ctrlp->fr, 8); + + sprintf(strBuffer, + "********************\nDrive Info\n********************\n"); + + fprintf(fpOutFile, "%s", strBuffer); + sprintf(strBuffer, + "%-20s : /dev/nvme%d\n%-20s : %s\n%-20s : %-20s\n%-20s : %-20s\n", + "Device Name", nFD, + "Model No", (char *)model, + "Serial No", (char *)serial, "FW-Rev", (char *)fwrev); + + fprintf(fpOutFile, "%s", strBuffer); + + sprintf(strBuffer, + "\n********************\nPCI Info\n********************\n"); + + fprintf(fpOutFile, "%s", strBuffer); + + sprintf(strBuffer, + "%-22s : %04X\n%-22s : %04X\n", + "VendorId", vendor_id, "DeviceId", device_id); + fprintf(fpOutFile, "%s", strBuffer); + fclose(fpOutFile); + free(strPDir); } -static eDriveModel GetDriveModel(int idx) +static void GetTimestampInfo(const char *strOSDirName) { - eDriveModel eModel = UNKNOWN_MODEL; - char path[512]; + __u8 outstr[1024]; + time_t t; + struct tm *tmp; + size_t num; + char *strPDir; + char *strDest; + + t = time(NULL); + tmp = localtime(&t); + if (tmp == NULL) + return; + + num = strftime((char *)outstr, sizeof(outstr), "Timestamp (UTC): %a, %d %b %Y %T %z", tmp); + num += sprintf((char *)(outstr + num), "\nPackage Version: 1.4"); + if (num) { + strPDir = strdup(strOSDirName); + strDest = dirname(strPDir); + WriteData(outstr, num, strDest, "timestamp_info.txt", "timestamp"); + free(strPDir); + } +} - sprintf(path, fvendorid1, idx); - if (ReadSysFile(path, &vendor_id) < 0) { - sprintf(path, fvendorid2, idx); - ReadSysFile(path, &vendor_id); - } - sprintf(path, fdeviceid1, idx); - if (ReadSysFile(path, &device_id) < 0) { - sprintf(path, fdeviceid2, idx); - ReadSysFile(path, &device_id); - } +static void GetCtrlIDDInfo(const char *dir, struct nvme_id_ctrl *ctrlp) +{ + WriteData((__u8*)ctrlp, sizeof(*ctrlp), dir, "nvme_controller_identify_data.bin", "id-ctrl"); +} - if (vendor_id == 0x1344) { - switch (device_id) { - case 0x5410: - eModel = M5410; - break; - case 0x51A0: - case 0x51A1: - case 0x51A2: - eModel = M51AX; - break; - case 0x51B0: - case 0x51B1: - case 0x51B2: - eModel = M51BX; - break; - default: - break; - } - } - return eModel; +static void GetSmartlogData(int fd, const char *dir) +{ + struct nvme_smart_log smart_log; + if (nvme_smart_log(fd, -1, &smart_log) == 0) { + WriteData((__u8*)&smart_log, sizeof(smart_log), dir, "smart_data.bin", "smart log"); + } } -static void GetDriveInfo(const char *strOSDirName, int nFD, - struct nvme_id_ctrl *ctrlp) +static void GetErrorlogData(int fd, int entries, const char *dir) { - FILE *fpOutFile = NULL; - char tempFile[256] = { 0 }; - char strBuffer[1024] = { 0 }; - char model[41] = { 0 }; - char serial[21] = { 0 }; - char fwrev[9] = { 0 }; - char *strPDir = strdup(strOSDirName); - char *strDest = dirname(strPDir); - - sprintf(tempFile, "%s/%s", strDest, "drive-info.txt"); - fpOutFile = fopen(tempFile, "w+"); - if (!fpOutFile) { - printf("Unable to create %s\n", tempFile); - free(strPDir); - return; - } + int logSize = entries * sizeof(struct nvme_error_log_page); + struct nvme_error_log_page *error_log = (struct nvme_error_log_page *)calloc(1, logSize); - strncpy(model, ctrlp->mn, 40); - strncpy(serial, ctrlp->sn, 20); - strncpy(fwrev, ctrlp->fr, 8); + if (error_log == NULL) + return; - sprintf(strBuffer, - "********************\nDrive Info\n********************\n"); + if (nvme_error_log(fd, entries, error_log) == 0) { + WriteData((__u8*)error_log, logSize, dir, "error_information_log.bin", "error log"); + } - fprintf(fpOutFile, "%s", strBuffer); - sprintf(strBuffer, - "%-20s : /dev/nvme%d\n%-20s : %s\n%-20s : %-20s\n%-20s : %-20s\n", - "Device Name", nFD, - "Model No", (char *)model, - "Serial No", (char *)serial, "FW-Rev", (char *)fwrev); + free(error_log); +} - fprintf(fpOutFile, "%s", strBuffer); +static void GetNSIDDInfo(int fd, const char *dir, int nsid) +{ + char file[PATH_MAX] = { 0 }; + struct nvme_id_ns ns; - sprintf(strBuffer, - "\n********************\nPCI Info\n********************\n"); + if (nvme_identify_ns(fd, nsid, 0, &ns) == 0) { + sprintf(file, "identify_namespace_%d_data.bin", nsid); + WriteData((__u8*)&ns, sizeof(ns), dir, file, "id-ns"); + } +} - fprintf(fpOutFile, "%s", strBuffer); +static void GetOSConfig(const char *strOSDirName) +{ + FILE *fpOSConfig = NULL; + char strBuffer[1024], strTemp[1024]; + char strFileName[PATH_MAX]; + int i; + + struct { + char *strcmdHeader; + char *strCommand; + } cmdArray[] = { + { (char *)"SYSTEM INFORMATION", (char *)"uname -a >> %s" }, + { (char *)"LINUX KERNEL MODULE INFORMATION", (char *)"lsmod >> %s" }, + { (char *)"LINUX SYSTEM MEMORY INFORMATION", (char *)"cat /proc/meminfo >> %s" }, + { (char *)"SYSTEM INTERRUPT INFORMATION", (char *)"cat /proc/interrupts >> %s" }, + { (char *)"CPU INFORMATION", (char *)"cat /proc/cpuinfo >> %s" }, + { (char *)"IO MEMORY MAP INFORMATION", (char *)"cat /proc/iomem >> %s" }, + { (char *)"MAJOR NUMBER AND DEVICE GROUP", (char *)"cat /proc/devices >> %s" }, + { (char *)"KERNEL DMESG", (char *)"dmesg >> %s" }, + { (char *)"/VAR/LOG/MESSAGES", (char *)"cat /var/log/messages >> %s" } + }; + + sprintf(strFileName, "%s/%s", strOSDirName, "os_config.txt"); + + for (i = 0; i < 7; i++) { + fpOSConfig = fopen(strFileName, "a+"); + fprintf(fpOSConfig, + "\n\n\n\n%s\n-----------------------------------------------\n", + cmdArray[i].strcmdHeader); + if (NULL != fpOSConfig) { + fclose(fpOSConfig); + fpOSConfig = NULL; + } + strcpy(strTemp, cmdArray[i].strCommand); + sprintf(strBuffer, strTemp, strFileName); + if (system(strBuffer)) + fprintf(stderr, "Failed to send \"%s\"\n", strBuffer); + } +} - sprintf(strBuffer, - "%-22s : %04X\n%-22s : %04X\n", - "VendorId", vendor_id, "DeviceId", device_id); - fprintf(fpOutFile, "%s", strBuffer); - fclose(fpOutFile); - free(strPDir); +static int micron_telemetry_log(int fd, __u8 gen, __u8 type, __u8 **data, int *logSize, int da) +{ + int err; + unsigned short data_area[4]; + unsigned char ctrl_init = (type == 0x8); + + __u8 *buffer = (unsigned char *)calloc(512, 1); + if (buffer == NULL) + return -1; + err = nvme_get_telemetry_log(fd, buffer, gen, ctrl_init, 512, 0); + if (err != 0) { + fprintf(stderr, "Failed to get telemetry log header for 0x%X\n", type); + if (buffer != NULL) { + free(buffer); + } + return err; + } + + // compute size of the log + data_area[1] = buffer[9] << 16 | buffer[8]; + data_area[2] = buffer[11] << 16 | buffer[10]; + data_area[3] = buffer[13] << 16 | buffer[12]; + data_area[0] = data_area[1] > data_area[2] ? data_area[1] : data_area[2]; + data_area[0] = data_area[3] > data_area[0] ? data_area[3] : data_area[0]; + + if (data_area[da] == 0) { + fprintf(stderr, "Requested telemetry data for 0x%X is empty\n", type); + if (buffer != NULL) { + free(buffer); + buffer = NULL; + } + return -1; + } + + *logSize = data_area[da] * 512; + if ((buffer = (unsigned char *)realloc(buffer, (size_t)(*logSize))) != NULL) { + err = nvme_get_telemetry_log(fd, buffer, gen, ctrl_init, *logSize, 0); + } + + if (err == 0 && buffer != NULL) { + *data = buffer; + } else { + fprintf(stderr, "Failed to get telemetry data for 0x%x\n", type); + if (buffer != NULL) + free(buffer); + } + + return err; } -static void GetTimestampInfo(const char *strOSDirName) +static int GetTelemetryData(int fd, const char *dir) { - char outstr[200]; - time_t t; - struct tm *tmp; - FILE *fpOutFile = NULL; - size_t num; - char tempFolder[256] = { 0 }; - char *strPDir; - char *strDest; - - t = time(NULL); - tmp = localtime(&t); - if (tmp == NULL) - return; - - num = strftime(outstr, sizeof(outstr), "Timestamp (UTC): %a, %d %b %Y %T %z", tmp); - if (num) { - strPDir = strdup(strOSDirName); - strDest = dirname(strPDir); - sprintf(tempFolder, "%s/%s", strDest, "timestamp_info.txt"); - fpOutFile = fopen(tempFolder, "wb"); - if (fwrite(outstr, 1, num, fpOutFile) != num) - printf("Unable to write to %s file!", tempFolder); - if (fpOutFile) - fclose(fpOutFile); - free(strPDir); - } + unsigned char *buffer = NULL; + int i, err, logSize = 0; + char msg[256] = {0}; + struct { + __u8 log; + char *file; + } tmap[] = { + {0x07, "nvme_host_telemetry.bin"}, + {0x08, "nvme_cntrl_telemetry.bin"}, + }; + + for(i = 0; i < (int)(sizeof(tmap)/sizeof(tmap[0])); i++) { + err = micron_telemetry_log(fd, 0, tmap[i].log, &buffer, &logSize, 0); + if (err == 0 && logSize > 0 && buffer != NULL) { + sprintf(msg, "telemetry log: 0x%X", tmap[i].log); + WriteData(buffer, logSize, dir, tmap[i].file, msg); + if (buffer != NULL) + free(buffer); + } + buffer = NULL; + logSize = 0; + } + return err; } -static void GetCtrlIDDInfo(const char *strCtrlDirName, struct nvme_id_ctrl *ctrlp) +static int GetFeatureSettings(int fd, const char *dir) { - char tempFolder[PATH_MAX] = { 0 }; - FILE *fpOutFile; - sprintf(tempFolder, "%s/%s", strCtrlDirName, - "nvme_controller_identify_data.bin"); - fpOutFile = fopen(tempFolder, "wb"); - if (fwrite(ctrlp, 1, sizeof(*ctrlp), fpOutFile) != sizeof(*ctrlp)) - printf("Unable to write controller data to %s file!", tempFolder); - if (fpOutFile) - fclose(fpOutFile); + unsigned char *bufp, buf[4096] = { 0 }; + int i, err, len, errcnt = 0; + __u32 attrVal = 0; + char msg[256] = { 0 }; + + struct features { + int id; + char *file; + } fmap[] = { + {0x01, "nvme_feature_setting_arbitration.bin"}, + {0x02, "nvme_feature_setting_pm.bin"}, + {0x03, "nvme_feature_setting_lba_range_namespace_1.bin"}, + {0x04, "nvme_feature_setting_temp_threshold.bin"}, + {0x05, "nvme_feature_setting_error_recovery.bin"}, + {0x06, "nvme_feature_setting_volatile_write_cache.bin"}, + {0x07, "nvme_feature_setting_num_queues.bin"}, + {0x08, "nvme_feature_setting_interrupt_coalescing.bin"}, + {0x09, "nvme_feature_setting_interrupt_vec_config.bin"}, + {0x0A, "nvme_feature_setting_write_atomicity.bin"}, + {0x0B, "nvme_feature_setting_async_event_config.bin"}, + {0x80, "nvme_feature_setting_sw_progress_marker.bin"}, + }; + + for (i = 0; i < (int)(sizeof(fmap)/sizeof(fmap[0])); i++) { + if (fmap[i].id == 0x03) { + len = 4096; + bufp = (unsigned char *)(&buf[0]); + } else { + len = 0; + bufp = NULL; + } + + err = nvme_get_feature(fd, 1, fmap[i].id, 0, 0x0, len, bufp, &attrVal); + if (err == 0) { + sprintf(msg, "feature: 0x%X", fmap[i].id); + WriteData((__u8*)&attrVal, sizeof(attrVal), dir, fmap[i].file, msg); + if (bufp != NULL) { + WriteData(bufp, len, dir, fmap[i].file, msg); + } + } else { + printf("Failed to retrieve feature 0x%x data !\n", fmap[i].id); + errcnt++; + } + } + return (int)(errcnt == sizeof(fmap)/sizeof(fmap[0])); } -static void GetSmartlogData(int fd, const char *strCtrlDirName) +static int micron_drive_info(int argc, char **argv, struct command *cmd, + struct plugin *plugin) { - char tempFolder[PATH_MAX] = { 0 }; - FILE *fpOutFile = NULL; - struct nvme_smart_log smart_log; - if (nvme_smart_log(fd, -1, &smart_log) == 0) { - sprintf(tempFolder, "%s/%s", strCtrlDirName, "smart_data.bin"); - fpOutFile = fopen(tempFolder, "wb"); - if (fwrite(&smart_log, 1, sizeof(smart_log), fpOutFile) != sizeof(smart_log)) - printf("Unable to write smart log data to %s file!", tempFolder); - if (fpOutFile) - fclose(fpOutFile); - } + printf("This command is not yet implemented\n"); + return 0; +} +static int micron_plugin_version(int argc, char **argv, struct command *cmd, + struct plugin *plugin) +{ + printf("nvme-cli Micron plugin version: %s.%s.%s\n", + __version_major, __version_minor, __version_patch); + return 0; } -static void GetErrorlogData(int fd, int entries, const char *strCtrlDirName) +static int micron_logpage_dir(int argc, char **argv, struct command *cmd, + struct plugin *plugin) { - char tempFolder[PATH_MAX] = { 0 }; - FILE *fpOutFile = NULL; - int logSize = entries * sizeof(struct nvme_error_log_page); - struct nvme_error_log_page *error_log = (struct nvme_error_log_page *)calloc(1, logSize); - - if (error_log == NULL) - return; - - if (nvme_error_log(fd, entries, error_log) == 0) { - sprintf(tempFolder, "%s/%s", strCtrlDirName, - "error_information_log.bin"); - fpOutFile = fopen(tempFolder, "wb"); - if (fwrite(error_log, 1, logSize, fpOutFile) != logSize) - printf("Unable to write error log to %s file!", tempFolder); - if (fpOutFile) - fclose(fpOutFile); - } - free(error_log); + printf("This command is not implemented for the drive\n"); + return 0; +} +static int micron_fw_activation_history(int argc, char **argv, struct command *cmd, + struct plugin *plugin) +{ + printf("This command is not implemented for the drive\n"); + return 0; +} +static int micron_error_reason(int argc, char **argv, struct command *cmd, + struct plugin *plugin) +{ + printf("This command is not implemented for the drive\n"); + return 0; +} +static int micron_ext_smart_logs(int argc, char **argv, struct command *cmd, + struct plugin *plugin) +{ + printf("This command is not implemented for the drive\n"); + return 0; } -static void GetNSIDDInfo(int fd, const char *strCtrlDirName, int nsid) +static int micron_clr_fw_activation_history(int argc, char **argv, struct command *cmd, + struct plugin *plugin) { - char tempFolder[256] = { 0 }; - char strFileName[PATH_MAX] = { 0 }; - FILE *fpOutFile = NULL; - struct nvme_id_ns ns; - if (nvme_identify_ns(fd, nsid, 0, &ns) == 0) { - sprintf(tempFolder, "identify_namespace_%d_data.bin.bin", nsid); - sprintf(strFileName, "%s/%s", strCtrlDirName, tempFolder); - fpOutFile = fopen(strFileName, "wb"); - if (fwrite(&ns, 1, sizeof(ns), fpOutFile) != sizeof(ns)) - printf("Unable to write controller data to %s file!", tempFolder); - if (fpOutFile) - fclose(fpOutFile); - } + const char *desc = "Clear FW activation history"; + int fd, err = 0; + __u32 result = 0; + __u8 fid = 0xCE; + eDriveModel model = UNKNOWN_MODEL; + OPT_ARGS(opts) = { + OPT_END() + }; + + if ((fd = micron_parse_options(argc, argv, desc, opts, &model)) < 0) + return err; + + if (model != M51CX) { + printf ("This option is not supported for specified drive\n"); + close(fd); + return err; + } + + //err = nvme_set_feature(fd, 1, fid, cdw11, 0, opt.save, 0, 0, &result); + err = nvme_set_feature(fd, 1, fid, 0, 0, 0, 0, 0, &result); + if (err == 0) err = (int)result; + return err; } -static void GetOSConfig(const char *strOSDirName) +static int micron_telemetry_cntrl_option(int argc, char **argv, struct command *cmd, + struct plugin *plugin) { - FILE *fpOSConfig = NULL; - char strBuffer[1024], strTemp[1024]; - char strFileName[PATH_MAX]; - int i; - - struct { - char *strcmdHeader; - char *strCommand; - } cmdArray[] = { - { (char *)"SYSTEM INFORMATION", (char *)"uname -a >> %s" }, - { (char *)"LINUX KERNEL MODULE INFORMATION", (char *)"lsmod >> %s" }, - { (char *)"LINUX SYSTEM MEMORY INFORMATION", (char *)"cat /proc/meminfo >> %s" }, - { (char *)"SYSTEM INTERRUPT INFORMATION", (char *)"cat /proc/interrupts >> %s" }, - { (char *)"CPU INFORMATION", (char *)"cat /proc/cpuinfo >> %s" }, - { (char *)"IO MEMORY MAP INFORMATION", (char *)"cat /proc/iomem >> %s" }, - { (char *)"MAJOR NUMBER AND DEVICE GROUP", (char *)"cat /proc/devices >> %s" }, - { (char *)"KERNEL DMESG", (char *)"dmesg >> %s" }, - { (char *)"/VAR/LOG/MESSAGES", (char *)"cat /var/log/messages >> %s" } - }; - - sprintf(strFileName, "%s/%s", strOSDirName, "os_config.txt"); - - for (i = 0; i < 7; i++) { - fpOSConfig = fopen(strFileName, "a+"); - fprintf(fpOSConfig, - "\n\n\n\n%s\n-----------------------------------------------\n", - cmdArray[i].strcmdHeader); - if (NULL != fpOSConfig) { - fclose(fpOSConfig); - fpOSConfig = NULL; - } - strcpy(strTemp, cmdArray[i].strCommand); - sprintf(strBuffer, strTemp, strFileName); - if (system(strBuffer)) - fprintf(stderr, "Failed to send \"%s\"\n", strBuffer); - } + int err = 0; + __u32 result = 0; + const char *desc = "Enable or Disable Controller telemetry log generation"; + const char *option = "enable or disable or status"; + const char *select = "select/save values: enable/disable options 1 - save (persistent), 0 - non-persistent and for status options: 0 - current, 1 - default, 2-saved"; + int fd = 0; + int fid = 0xCF; + eDriveModel model = UNKNOWN_MODEL; + struct nvme_id_ctrl ctrl = { 0 }; + + struct { + char *option; + int select; + } opt = { + .option = "disable", + .select= 0, + }; + + OPT_ARGS(opts) = { + OPT_STRING("option", 'o', "option", &opt.option, option), + OPT_UINT("select", 's', &opt.select, select), + OPT_END() + }; + + if ((fd = micron_parse_options(argc, argv, desc, opts, &model)) < 0) { + return -1; + } + + err = nvme_identify_ctrl(fd, &ctrl); + if ((ctrl.lpa & 0x8) != 0x8) { + printf("drive doesn't support host/controller generated telemetry logs\n"); + close(fd); + return err; + } + +#if 0 + if (model != M51CX && model != M5407 && model != M5411) { + printf ("This option is not supported for specified drive\n"); + close(fd); + return err; + } +#endif + + if (!strcmp(opt.option, "enable")) { + err = nvme_set_feature(fd, 1, fid, 1, 0, (opt.select & 0x1), 0, 0, &result); + if (err == 0) { + printf("successfully set controller telemetry option\n"); + } else { + printf("Failed to set controller telemetry option\n"); + } + } else if (strcmp(opt.option, "disable")) { + err = nvme_set_feature(fd, 1, fid, 0, 0, (opt.select & 0x1), 0, 0, &result); + if (err == 0) { + printf("successfully disabled controller telemetry option\n"); + } else { + printf("Failed to disable controller telemetry option\n"); + } + } else if (!strcmp(opt.option, "status")) { + opt.select &= 0x3; + err = nvme_get_feature(fd, 1, fid, opt.select, 0, 0, 0, &result); + if (err == 0) { + printf("Controller telemetry option : %s\n", + (result) ? "enabled" : "disabled"); + } else { + printf("Failed to retrieve controller telemetry option\n"); + } + } else { + printf("invalid option %s, valid values are enable,disable or status\n", opt.option); + close(fd); + return -1; + } + + close(fd); + return err; } static int micron_internal_logs(int argc, char **argv, struct command *cmd, - struct plugin *plugin) + struct plugin *plugin) { - - int err = 0; - int fd; - int ctrlIdx; - FILE *fpOutFile = NULL; - char strOSDirName[1024]; - char strCtrlDirName[1024]; - char strMainDirName[256]; - char tempFolder[PATH_MAX] = { 0 }; - unsigned int *puiIDDBuf; - unsigned int uiMask; - struct nvme_id_ctrl ctrl; - char sn[20] = { 0 }; - - struct { - unsigned char ucLogPage; - const char *strFileName; - int nLogSize; - int nMaxSize; - } aVendorLogs[32] = { - { 0xC1, "nvmelog_C1.bin", 0, 0 }, - { 0xC2, "nvmelog_C2.bin", 0, 0 }, - { 0xC4, "nvmelog_C4.bin", 0, 0 }, - { 0xC5, "nvmelog_C5.bin", C5_log_size, 0 }, - { 0xD0, "nvmelog_D0.bin", D0_log_size, 0 }, - { 0xE6, "nvmelog_E6.bin", 0, 0 }, - { 0xE7, "nvmelog_E7.bin", 0, 0 } - }, - aM51XXLogs[] = { - { 0xFB, "nvmelog_FB.bin", 4096, 0 }, /* this should be collected first for M51AX */ - { 0xF7, "nvmelog_F7.bin", 4096, 512 * 1024 }, - { 0xF8, "nvmelog_F8.bin", 4096, 512 * 1024 }, - { 0xF9, "nvmelog_F9.bin", 4096, 200 * 1024 * 1024 }, - { 0xFC, "nvmelog_FC.bin", 4096, 200 * 1024 * 1024 }, - { 0xFD, "nvmelog_FD.bin", 4096, 80 * 1024 * 1024 } - }, - aM51AXLogs[] = { - { 0xD0, "nvmelog_D0.bin", 512, 0 }, - { 0xCA, "nvmelog_CA.bin", 512, 0 }, - { 0xFA, "nvmelog_FA.bin", 4096, 15232 }, - { 0xFE, "nvmelog_FE.bin", 4096, 512 * 1024 }, - { 0xFF, "nvmelog_FF.bin", 4096, 162 * 1024 }, - { 0x04, "changed_namespace_log.bin", 4096, 0 }, - { 0x05, "command_effects_log.bin", 4096, 0 }, - { 0x06, "drive_self_test.bin", 4096, 0 } - }, - aM51BXLogs[] = { - { 0xFA, "nvmelog_FA.bin", 4096, 16376 }, - { 0xFE, "nvmelog_FE.bin", 4096, 256 * 1024 }, - { 0xFF, "nvmelog_FF.bin", 4096, 64 * 1024 }, - { 0xCA, "nvmelog_CA.bin", 512, 1024 } - }; - - eDriveModel eModel; - - const char *desc = "This retrieves the micron debug log package"; - const char *package = "Log output package name (required)"; - unsigned char *dataBuffer = NULL; - int bSize = 0; - int maxSize = 0; - - struct config { - char *package; - }; - - struct config cfg = { - .package = "" - }; - - OPT_ARGS(opts) = { - OPT_STRING("package", 'p', "FILE", &cfg.package, package), - OPT_END() - }; - - fd = parse_and_open(argc, argv, desc, opts); - - if (strlen(cfg.package) == 0) { - printf ("You must specify an output name for the log package. ie --p=logfiles.zip\n"); - goto out; - } - - if (fd < 0) - goto out; - - /* pull log details based on the model name */ - sscanf(argv[optind], "/dev/nvme%d", &ctrlIdx); - if ((eModel = GetDriveModel(ctrlIdx)) == UNKNOWN_MODEL) { - printf ("Unsupported drive model for vs-internal-log collection\n"); - close(fd); - goto out; - } - - printf("Preparing log package. This will take a few seconds...\n"); - err = nvme_identify_ctrl(fd, &ctrl); - if (err) - goto out; - - // trim spaces out of serial number string */ - int i, j = 0; - for (i = 0; i < sizeof(ctrl.sn); i++) { - if (isblank(ctrl.sn[i])) - continue; - sn[j++] = ctrl.sn[i]; - } - sn[j] = '\0'; - strcpy(ctrl.sn, sn); - - SetupDebugDataDirectories(ctrl.sn, cfg.package, strMainDirName, strOSDirName, strCtrlDirName); - - GetTimestampInfo(strOSDirName); - GetCtrlIDDInfo(strCtrlDirName, &ctrl); - GetOSConfig(strOSDirName); - GetDriveInfo(strOSDirName, ctrlIdx, &ctrl); - - for (int i = 1; i <= ctrl.nn; i++) - GetNSIDDInfo(fd, strCtrlDirName, i); - - GetSmartlogData(fd, strCtrlDirName); - GetErrorlogData(fd, ctrl.elpe, strCtrlDirName); - - if (eModel != M5410) { - memcpy(aVendorLogs, aM51XXLogs, sizeof(aM51XXLogs)); - if (eModel == M51AX) - memcpy((char *)aVendorLogs + sizeof(aM51XXLogs), aM51AXLogs, sizeof(aM51AXLogs)); - else - memcpy((char *)aVendorLogs + sizeof(aM51XXLogs), aM51BXLogs, sizeof(aM51BXLogs)); - } - - for (int i = 0; i < (int)(sizeof(aVendorLogs) / sizeof(aVendorLogs[0])) && aVendorLogs[i].ucLogPage != 0; i++) { - err = -1; - switch (aVendorLogs[i].ucLogPage) { - case 0xC1: - case 0xC2: - case 0xC4: - err = GetLogPageSize(fd, aVendorLogs[i].ucLogPage, &bSize); - if (err == 0 && bSize > 0) - err = GetCommonLogPage(fd, aVendorLogs[i].ucLogPage, &dataBuffer, bSize); - break; - - case 0xE6: - case 0xE7: - puiIDDBuf = (unsigned int *)&ctrl; - uiMask = puiIDDBuf[1015]; - if (uiMask == 0 || (aVendorLogs[i].ucLogPage == 0xE6 && uiMask == 2) || (aVendorLogs[i].ucLogPage == 0xE7 - && uiMask == 1)) { - bSize = 0; - } else { - bSize = (int)puiIDDBuf[1015]; - if (bSize % (16 * 1024)) { - bSize += (16 * 1024) - (bSize % (16 * 1024)); - } - } - if (bSize != 0) { - dataBuffer = (unsigned char *)malloc(bSize); - memset(dataBuffer, 0, bSize); - err = NVMEGetLogPage(fd, aVendorLogs[i].ucLogPage, dataBuffer, bSize); - } - break; - - case 0xF7: - case 0xF9: - case 0xFC: - case 0xFD: - if (eModel == M51BX) - (void)NVMEResetLog(fd, aVendorLogs[i].ucLogPage, aVendorLogs[i].nLogSize, aVendorLogs[i].nMaxSize); - default: - bSize = aVendorLogs[i].nLogSize; - dataBuffer = (unsigned char *)malloc(bSize); - memset(dataBuffer, 0, bSize); - err = NVMEGetLogPage(fd, aVendorLogs[i].ucLogPage, dataBuffer, bSize); - maxSize = aVendorLogs[i].nMaxSize - bSize; - while (err == 0 && maxSize > 0 && ((unsigned int *)dataBuffer)[0] != 0xdeadbeef) { - sprintf(tempFolder, "%s/%s", strCtrlDirName, - aVendorLogs[i].strFileName); - fpOutFile = fopen(tempFolder, "ab+"); - if (fwrite(dataBuffer, 1, bSize, fpOutFile) != bSize) { - printf ("Unable to write log to file %s\n!", aVendorLogs[i].strFileName); - } - if (fpOutFile) - fclose(fpOutFile); - err = NVMEGetLogPage(fd, aVendorLogs[i].ucLogPage, dataBuffer, bSize); - if (err || (((unsigned int *)dataBuffer)[0] == 0xdeadbeef)) - break; - maxSize -= bSize; - } - break; - } - - if (err == 0 && dataBuffer != NULL && ((unsigned int *)dataBuffer)[0] != 0xdeadbeef) { - sprintf(tempFolder, "%s/%s", strCtrlDirName, - aVendorLogs[i].strFileName); - fpOutFile = fopen(tempFolder, "ab+"); - if (fwrite(dataBuffer, 1, bSize, fpOutFile) != bSize) { - printf("Unable to write log to file %s\n!", aVendorLogs[i].strFileName); - } - if (fpOutFile) - fclose(fpOutFile); - } - - if (dataBuffer != NULL) { - free(dataBuffer); - dataBuffer = NULL; - } - } - - ZipAndRemoveDir(strMainDirName, cfg.package); - out: - return err; + int err = 0; + int fd = 0; + int ctrlIdx, telemetry_option = 0; + char strOSDirName[1024]; + char strCtrlDirName[1024]; + char strMainDirName[256]; + unsigned int *puiIDDBuf; + unsigned int uiMask; + struct nvme_id_ctrl ctrl; + char sn[20] = { 0 }; + char msg[256] = { 0 }; + + struct { + unsigned char ucLogPage; + const char *strFileName; + int nLogSize; + int nMaxSize; + } aVendorLogs[32] = { + { 0x03, "firmware_slot_info_log.bin", 512, 0 }, + { 0xC1, "nvmelog_C1.bin", 0, 0 }, + { 0xC2, "nvmelog_C2.bin", 0, 0 }, + { 0xC4, "nvmelog_C4.bin", 0, 0 }, + { 0xC5, "nvmelog_C5.bin", C5_log_size, 0 }, + { 0xD0, "nvmelog_D0.bin", D0_log_size, 0 }, + { 0xE6, "nvmelog_E6.bin", 0, 0 }, + { 0xE7, "nvmelog_E7.bin", 0, 0 } + }, + aM51XXLogs[] = { + { 0xFB, "nvmelog_FB.bin", 4096, 0 }, /* this should be collected first for M51AX */ + { 0xD0, "nvmelog_D0.bin", 512, 0 }, + { 0x03, "firmware_slot_info_log.bin", 512, 0}, + { 0xF7, "nvmelog_F7.bin", 4096, 512 * 1024 }, + { 0xF8, "nvmelog_F8.bin", 4096, 512 * 1024 }, + { 0xF9, "nvmelog_F9.bin", 4096, 200 * 1024 * 1024 }, + { 0xFC, "nvmelog_FC.bin", 4096, 200 * 1024 * 1024 }, + { 0xFD, "nvmelog_FD.bin", 4096, 80 * 1024 * 1024 } + }, + aM51AXLogs[] = { + { 0xCA, "nvmelog_CA.bin", 512, 0 }, + { 0xFA, "nvmelog_FA.bin", 4096, 15232 }, + { 0xF6, "nvmelog_F6.bin", 4096, 512 * 1024 }, + { 0xFE, "nvmelog_FE.bin", 4096, 512 * 1024 }, + { 0xFF, "nvmelog_FF.bin", 4096, 162 * 1024 }, + { 0x04, "changed_namespace_log.bin", 4096, 0 }, + { 0x05, "command_effects_log.bin", 4096, 0 }, + { 0x06, "drive_self_test.bin", 4096, 0 } + }, + aM51BXLogs[] = { + { 0xFA, "nvmelog_FA.bin", 4096, 16376 }, + { 0xFE, "nvmelog_FE.bin", 4096, 256 * 1024 }, + { 0xFF, "nvmelog_FF.bin", 4096, 64 * 1024 }, + { 0xCA, "nvmelog_CA.bin", 512, 1024 } + }; + + eDriveModel eModel; + + const char *desc = "This retrieves the micron debug log package"; + const char *package = "Log output data file name (required)"; + const char *type = "telemetry log type - host or controller"; + const char *data_area = "telemetry log data area 1, 2 or 3"; + unsigned char *dataBuffer = NULL; + int bSize = 0; + int maxSize = 0; + + struct config { + char *type; + char *package; + int data_area; + int log; + }; + + struct config cfg = { + .type = "", + .package = "", + .data_area = -1, + .log = 0x07, + }; + + OPT_ARGS(opts) = { + OPT_STRING("type", 't', "log type", &cfg.type, type), + OPT_STRING("package", 'p', "FILE", &cfg.package, package), + OPT_UINT("data_area", 'd', &cfg.data_area, data_area), + OPT_END() + }; + + fd = parse_and_open(argc, argv, desc, opts); + + if (fd < 0) + goto out; + + // if telemetry type is specified, check for data area + if (strlen(cfg.type) != 0) { + if (!strcmp(cfg.type, "controller")) { + cfg.log = 0x08; + } else if (strcmp(cfg.type, "host")) { + printf ("telemetry type (host or controller) should be specified i.e. -t=host\n"); + close(fd); + goto out; + } + + if (cfg.data_area <= 0 || cfg.data_area > 3) { + printf ("data area must be selected using -d option ie --d=1,2,3\n"); + close(fd); + goto out; + } + telemetry_option = 1; + } else if (cfg.data_area > 0) { + printf ("data area option is valid only for telemetry option (i.e --type=host|controller)\n"); + close(fd); + goto out; + } + + if (strlen(cfg.package) == 0) { + if (telemetry_option) + printf ("Output file name for the log data must be specified. ie -p=logfile.bin\n"); + else + printf ("Output file name for the log data must be specified. ie -p=logfile.zip\n"); + goto out; + } + + /* pull log details based on the model name */ + sscanf(argv[optind], "/dev/nvme%d", &ctrlIdx); + if ((eModel = GetDriveModel(ctrlIdx)) == UNKNOWN_MODEL) { + printf ("Unsupported drive model for vs-internal-log collection\n"); + close(fd); + goto out; + } + + err = nvme_identify_ctrl(fd, &ctrl); + if (err) + goto out; + + if (telemetry_option) { + if ((ctrl.lpa & 0x8) != 0x8) { + printf("telemetry option is not supported for specified drive\n"); + close(fd); + goto out; + } + int logSize = 0; __u8 *buffer = NULL; const char *dir = "."; + err = micron_telemetry_log(fd, 0, cfg.log, &buffer, &logSize, cfg.data_area); + if (err == 0 && logSize > 0 && buffer != NULL) { + sprintf(msg, "telemetry log: 0x%X", cfg.log); + WriteData(buffer, logSize, dir, cfg.package, msg); + free(buffer); + } + close(fd); + goto out; + } + + printf("Preparing log package. This will take a few seconds...\n"); + + // trim spaces out of serial number string */ + int i, j = 0; + for (i = 0; i < sizeof(ctrl.sn); i++) { + if (isblank(ctrl.sn[i])) + continue; + sn[j++] = ctrl.sn[i]; + } + sn[j] = '\0'; + strcpy(ctrl.sn, sn); + + SetupDebugDataDirectories(ctrl.sn, cfg.package, strMainDirName, strOSDirName, strCtrlDirName); + + GetTimestampInfo(strOSDirName); + GetCtrlIDDInfo(strCtrlDirName, &ctrl); + GetOSConfig(strOSDirName); + GetDriveInfo(strOSDirName, ctrlIdx, &ctrl); + + for (int i = 1; i <= ctrl.nn; i++) + GetNSIDDInfo(fd, strCtrlDirName, i); + + GetSmartlogData(fd, strCtrlDirName); + GetErrorlogData(fd, ctrl.elpe, strCtrlDirName); + + // pull if telemetry log data is supported + if ((ctrl.lpa & 0x8) == 0x8) + GetTelemetryData(fd, strCtrlDirName); + + GetFeatureSettings(fd, strCtrlDirName); + + if (eModel != M5410) { + memcpy(aVendorLogs, aM51XXLogs, sizeof(aM51XXLogs)); + if (eModel == M51AX) + memcpy((char *)aVendorLogs + sizeof(aM51XXLogs), aM51AXLogs, sizeof(aM51AXLogs)); + else + memcpy((char *)aVendorLogs + sizeof(aM51XXLogs), aM51BXLogs, sizeof(aM51BXLogs)); + } + + for (int i = 0; i < (int)(sizeof(aVendorLogs) / sizeof(aVendorLogs[0])) && aVendorLogs[i].ucLogPage != 0; i++) { + err = -1; + switch (aVendorLogs[i].ucLogPage) { + case 0xC1: + case 0xC2: + case 0xC4: + err = GetLogPageSize(fd, aVendorLogs[i].ucLogPage, &bSize); + if (err == 0 && bSize > 0) + err = GetCommonLogPage(fd, aVendorLogs[i].ucLogPage, &dataBuffer, bSize); + break; + + case 0xE6: + case 0xE7: + puiIDDBuf = (unsigned int *)&ctrl; + uiMask = puiIDDBuf[1015]; + if (uiMask == 0 || (aVendorLogs[i].ucLogPage == 0xE6 && uiMask == 2) || (aVendorLogs[i].ucLogPage == 0xE7 + && uiMask == 1)) { + bSize = 0; + } else { + bSize = (int)puiIDDBuf[1023]; + if (bSize % (16 * 1024)) { + bSize += (16 * 1024) - (bSize % (16 * 1024)); + } + } + if (bSize != 0 && (dataBuffer = (unsigned char *)malloc(bSize)) != NULL) { + memset(dataBuffer, 0, bSize); + err = nvme_get_log(fd, NVME_NSID_ALL, aVendorLogs[i].ucLogPage, false, bSize, dataBuffer); + } + break; + + case 0xF7: + case 0xF9: + case 0xFC: + case 0xFD: + if (eModel == M51BX) + (void)NVMEResetLog(fd, aVendorLogs[i].ucLogPage, aVendorLogs[i].nLogSize, aVendorLogs[i].nMaxSize); + default: + bSize = aVendorLogs[i].nLogSize; + dataBuffer = (unsigned char *)malloc(bSize); + if (dataBuffer == NULL) { + break; + } + memset(dataBuffer, 0, bSize); + err = nvme_get_log(fd, NVME_NSID_ALL, aVendorLogs[i].ucLogPage, false, bSize, dataBuffer); + maxSize = aVendorLogs[i].nMaxSize - bSize; + while (err == 0 && maxSize > 0 && ((unsigned int *)dataBuffer)[0] != 0xdeadbeef) { + sprintf(msg, "log 0x%x", aVendorLogs[i].ucLogPage); + WriteData(dataBuffer, bSize, strCtrlDirName, aVendorLogs[i].strFileName, msg); + err = nvme_get_log(fd, NVME_NSID_ALL, aVendorLogs[i].ucLogPage, false, bSize, dataBuffer); + if (err || (((unsigned int *)dataBuffer)[0] == 0xdeadbeef)) + break; + maxSize -= bSize; + } + break; + } + + if (err == 0 && dataBuffer != NULL && ((unsigned int *)dataBuffer)[0] != 0xdeadbeef) { + sprintf(msg, "log 0x%x", aVendorLogs[i].ucLogPage); + WriteData(dataBuffer, bSize, strCtrlDirName, aVendorLogs[i].strFileName, msg); + } + + if (dataBuffer != NULL) { + free(dataBuffer); + dataBuffer = NULL; + } + } + + ZipAndRemoveDir(strMainDirName, cfg.package); +out: + return err; } diff --git a/plugins/micron/micron-nvme.h b/plugins/micron/micron-nvme.h index add36e42..243a5d6d 100644 --- a/plugins/micron/micron-nvme.h +++ b/plugins/micron/micron-nvme.h @@ -12,7 +12,16 @@ PLUGIN(NAME("micron", "Micron vendor specific extensions"), ENTRY("vs-pcie-stats", "Retrieve Micron PCIe error stats", micron_pcie_stats) ENTRY("clear-pcie-correctable-errors", "Clear correctable PCIe errors", micron_clear_pcie_correctable_errors) ENTRY("vs-internal-log", "Retrieve Micron logs", micron_internal_logs) + ENTRY("vs-telemetry-controller-option", "Enable/Disable controller telemetry log generation", micron_telemetry_cntrl_option) ENTRY("vs-nand-stats", "Retrieve NAND Stats", micron_nand_stats) + ENTRY("vs-drive-info", "Retrieve Drive information", micron_drive_info) + ENTRY("plugin-version", "Display plugin version info", micron_plugin_version) + ENTRY("log-page-directory", "Retrieve log page directory", micron_logpage_dir) + ENTRY("vs-fw-activate-history", "Display FW activation history", micron_fw_activation_history) + ENTRY("vs-error-reason-identifier", "Retrieve Error reason", micron_error_reason) + ENTRY("vs-smart-add-log", "Retrieve extended SMART data", micron_ext_smart_logs) + ENTRY("clear-fw-activate-history", "Clear FW activation history", micron_clr_fw_activation_history) + ENTRY("vs-smbus-option", "Enable/Disable SMBUS on the drive", micron_smbus_option) ) ); -- 2.50.1