Поиск данных NVMe S. M. A. R. T.

#windows #powershell #wmi #hard-drive

Вопрос:

Я разработал программу, которая способна извлекать интеллектуальные данные для устройств SATA с помощью WMI примерно так: Get-WmiObject -Namespace 'RootWMI' -Class 'MSStorageDriver_ATAPISMartData' | Select -ExpandProperty Vendorspecific однако она не может получить данные NVMe. Кто — нибудь из вас знает, как его вернуть? (Даже если это не командлет Powershell)

По-видимому, документы Microsoft решили эту проблему: https://docs.microsoft.com/en-us/windows/win32/fileio/working-with-nvme-devices однако я не могу выполнить код, если кому-нибудь удастся его решить и отправить мне исходный код, я был бы признателен

Комментарии:

1. Действительно ли ваше устройство NVMe возвращает какой-либо СМАРТ-код, например Ubuntu LiveDVD? Если нет, то ты не сможешь. В противном случае вам нужен соответствующий класс, так как NVMe определенно НЕ является ATAPI.

2. Он возвращает данные S. M. A. R. T. через smartmontools, crystaldisk и т. Д. Моя модель диска: Intel SSDPEDMW012T4 1,2 ТБ

3. В соответствии с этим вам нужно собрать элементы MSStorageDriver_FailurePredictData класса из rootwmi пространства имен. Пока не могу это проверить, так как у меня нет физического хоста Windows, администратором которого я являюсь, пожалуйста, попробуйте.

4. Он не использует NVMe в качестве накопителя и не отображает никакой информации о нем. Я пробовал это на нескольких машинах, и единственными дисками, на которых не отображается информация, являются NVM, поэтому я думаю, что есть какой-то другой способ считывания их интеллектуальных данных.

Ответ №1:

Я только что обнаружил, что у Microsoft на самом деле есть неплохая документация по этой проблеме. Таким образом, способ извлечения S. M. A. R. T. из NVMe в C заключается в следующем:

мое определение для wszDrive таково: #define wszDrive L"\\.\PhysicalDrive1"

Вы можете указать номер диска, начиная с 0 (для первого диска).

 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    resu<
    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,
        amp;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 = amp;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: