windowspowershellwmihard-drive

NVMe S.M.A.R.T. data retrieval


I have developed a program which is able to retrieve the smart data for SATA devices with the help of WMI like so: Get-WmiObject -Namespace 'Root\WMI' -Class 'MSStorageDriver_ATAPISMartData' | Select -ExpandProperty Vendorspecific however, it is not able to get the NVMe's one. Any of you know some way to retrieve it? (Even if it is not a Powershell cmdlet)

Apparently Microsoft docs have addressed the issue: https://learn.microsoft.com/en-us/windows/win32/fileio/working-with-nvme-devices however, I am unable to execute the code if anyone manages to solve it and send me source code it would be appreciated


Solution

  • Microsoft has published documentation on Working with NVMe drives. The way you retrieve S.M.A.R.T. data from NVMe in C++ is the following:

    my definition for the wszDrive is: #define wszDrive L"\\\\.\\PhysicalDrive1"

    You can specify the number of the drive starting from 0 (for the first drive)

    HANDLE hDevice = INVALID_HANDLE_VALUE;  // handle to the drive to be examined 
    
        hDevice = CreateFileW((LPWSTR)wszDrive,          // drive to open
            0,                // no access to the drive
            FILE_SHARE_READ | // share mode
            FILE_SHARE_WRITE,
            NULL,             // default security attributes
            OPEN_EXISTING,    // disposition
            FILE_FLAG_OVERLAPPED,                // file attributes
            NULL);            // do not copy file attributes
    
        if (hDevice == INVALID_HANDLE_VALUE)    // cannot open the drive
        {
            return (FALSE);
        }
    
        BOOL    result;
        PVOID   buffer = NULL;
        ULONG   bufferLength = 0;
        ULONG   returnedLength = 0;
    
        PSTORAGE_PROPERTY_QUERY query = NULL;
        PSTORAGE_PROTOCOL_SPECIFIC_DATA protocolData = NULL;
        PSTORAGE_PROTOCOL_DATA_DESCRIPTOR protocolDataDescr = NULL;
    
        //
        // Allocate buffer for use.
        //
        bufferLength = FIELD_OFFSET(STORAGE_PROPERTY_QUERY, AdditionalParameters) + sizeof(STORAGE_PROTOCOL_SPECIFIC_DATA) + NVME_MAX_LOG_SIZE;
        buffer = malloc(bufferLength);
    
        if (buffer == NULL) {
            _tprintf(_T("DeviceNVMeQueryProtocolDataTest: allocate buffer failed, exit.\n"));
            goto exit;
        }
    
        //
        // Initialize query data structure to get Identify Controller Data.
        //
            ZeroMemory(buffer, bufferLength);
    
        query = (PSTORAGE_PROPERTY_QUERY)buffer;
        protocolDataDescr = (PSTORAGE_PROTOCOL_DATA_DESCRIPTOR)buffer;
        protocolData = (PSTORAGE_PROTOCOL_SPECIFIC_DATA)query->AdditionalParameters;
    
        query->PropertyId = StorageDeviceProtocolSpecificProperty;
        query->QueryType = PropertyStandardQuery;
    
        protocolData->ProtocolType = ProtocolTypeNvme;
        protocolData->DataType = NVMeDataTypeLogPage;
        protocolData->ProtocolDataRequestValue = NVME_LOG_PAGE_HEALTH_INFO;
        protocolData->ProtocolDataRequestSubValue = 0;  // This will be passed as the lower 32 bit of log page offset if controller supports extended data for the Get Log Page.
        protocolData->ProtocolDataRequestSubValue2 = 0; // This will be passed as the higher 32 bit of log page offset if controller supports extended data for the Get Log Page.
        protocolData->ProtocolDataRequestSubValue3 = 0; // This will be passed as Log Specific Identifier in CDW11.
    
        protocolData->ProtocolDataOffset = sizeof(STORAGE_PROTOCOL_SPECIFIC_DATA);
        protocolData->ProtocolDataLength = sizeof(NVME_HEALTH_INFO_LOG);
    
        //  
        // Send request down.  
        //  
        result = DeviceIoControl(hDevice,
            IOCTL_STORAGE_QUERY_PROPERTY,
            buffer,
            bufferLength,
            buffer,
            bufferLength,
            &returnedLength,
            NULL
        );
    
        if (!result || (returnedLength == 0)) {
            _tprintf(_T("DeviceNVMeQueryProtocolDataTest: SMART/Health Information Log failed. Error Code %d.\n"), GetLastError());
            goto exit;
        }
    
            //
            // Validate the returned data.
            //
            if ((protocolDataDescr->Version != sizeof(STORAGE_PROTOCOL_DATA_DESCRIPTOR)) ||
                (protocolDataDescr->Size != sizeof(STORAGE_PROTOCOL_DATA_DESCRIPTOR))) {
                _tprintf(_T("DeviceNVMeQueryProtocolDataTest: SMART/Health Information Log - data descriptor header not valid.\n"));
                return 0;
            }
    
        protocolData = &protocolDataDescr->ProtocolSpecificData;
    
        if ((protocolData->ProtocolDataOffset < sizeof(STORAGE_PROTOCOL_SPECIFIC_DATA)) ||
            (protocolData->ProtocolDataLength < sizeof(NVME_HEALTH_INFO_LOG))) {
            _tprintf(_T("DeviceNVMeQueryProtocolDataTest: SMART/Health Information Log - ProtocolData Offset/Length not valid.\n"));
            goto exit;
        }
    
            //
            // SMART/Health Information Log Data 
            //
        {
            PNVME_HEALTH_INFO_LOG smartInfo = (PNVME_HEALTH_INFO_LOG)((PCHAR)protocolData + protocolData->ProtocolDataOffset);
    
    // print the S.M.A.R.T. data which you need
    
            _tprintf(_T("DeviceNVMeQueryProtocolDataTest: SMART/Health Information Log Data - Temperature %d.\n"), ((ULONG)smartInfo->Temperature[1] << 8 | smartInfo->Temperature[0]) - 273);
            _tprintf(_T("DeviceNVMeQueryProtocolDataTest: SMART/Health Information Log Data - Available Spares: %d.\n"), ((ULONG)smartInfo->AvailableSpare));
    
            _tprintf(_T("DeviceNVMeQueryProtocolDataTest: ***SMART/Health Information Log succeeded***.\n"));
        }
        exit: