Hello @Ruben ,
To get the UFS_DEVICE_HEALTH_DESCRIPTOR, you need to use IOCTL_STORAGE_QUERY_PROPERTY with StorageAdapterProtocolSpecificProperty (or StorageDeviceProtocolSpecificProperty), and carefully set up the STORAGE_PROTOCOL_SPECIFIC_DATA appended to your STORAGE_PROPERTY_QUERY.
For UFS, the key parameters are:
-
ProtocolType=ProtocolTypeUfs -
DataType=UfsDataTypeDescriptor -
ProtocolDataRequestValue=UFS_DESC_HEALTH_ID(which is0x09)
Below is a workaround with C++ example of how to build the request and parse the response. Since this requires direct hardware access, make sure to run the compiled executable as an Administrator on the target device equipped with the UFS storage.
#include <windows.h>
#include <winioctl.h>
#include <ntddstor.h>
#include <ufs.h>
#include <iostream>
int main()
{
// Replace with your actual physical drive path
const char* drivePath = "\\\\.\\PhysicalDrive0";
HANDLE hDevice = CreateFileA(
drivePath,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
0,
NULL
);
if (hDevice == INVALID_HANDLE_VALUE) {
std::cerr << "Failed to open device. Error: " << GetLastError() << "\n";
return 1;
}
// Allocate a buffer large enough for the query, the protocol specific data, and the expected output descriptor
const DWORD BUFFER_SIZE = 1024;
BYTE buffer[BUFFER_SIZE] = { 0 };
// 1. Setup the query structure
PSTORAGE_PROPERTY_QUERY query = (PSTORAGE_PROPERTY_QUERY)buffer;
query->PropertyId = StorageAdapterProtocolSpecificProperty;
query->QueryType = PropertyStandardQuery;
// 2. Setup the protocol specific data
PSTORAGE_PROTOCOL_SPECIFIC_DATA protocolData = (PSTORAGE_PROTOCOL_SPECIFIC_DATA)query->AdditionalParameters;
protocolData->ProtocolType = ProtocolTypeUfs;
protocolData->DataType = UfsDataTypeDescriptor;
protocolData->ProtocolDataRequestValue = UFS_DESC_HEALTH_ID; // 0x09
protocolData->ProtocolDataRequestSubValue = 0;
protocolData->ProtocolDataOffset = sizeof(STORAGE_PROTOCOL_SPECIFIC_DATA);
protocolData->ProtocolDataLength = sizeof(UFS_DEVICE_HEALTH_DESCRIPTOR);
// Calculate the input size (Query header + Protocol specific data header)
DWORD inputSize = offsetof(STORAGE_PROPERTY_QUERY, AdditionalParameters) + sizeof(STORAGE_PROTOCOL_SPECIFIC_DATA);
DWORD bytesReturned = 0;
// 3. Send the request
BOOL result = DeviceIoControl(
hDevice,
IOCTL_STORAGE_QUERY_PROPERTY,
buffer,
inputSize,
buffer,
BUFFER_SIZE,
&bytesReturned,
NULL
);
if (!result) {
DWORD err = GetLastError();
std::cerr << "DeviceIoControl failed. Error: " << err << "\n";
if (err == ERROR_NOT_SUPPORTED || err == ERROR_INVALID_FUNCTION) {
std::cerr << "Note: The driver or device does not support this UFS protocol request (Are you sure it's a UFS drive?).\n";
}
CloseHandle(hDevice);
return 1;
}
// 4. Parse the response
PSTORAGE_PROTOCOL_DATA_DESCRIPTOR desc = (PSTORAGE_PROTOCOL_DATA_DESCRIPTOR)buffer;
PSTORAGE_PROTOCOL_SPECIFIC_DATA outProtocolData = &desc->ProtocolSpecificData;
// The actual health descriptor is located at the specified offset
PUFS_DEVICE_HEALTH_DESCRIPTOR healthData = (PUFS_DEVICE_HEALTH_DESCRIPTOR)((PBYTE)outProtocolData + outProtocolData->ProtocolDataOffset);
// 5. Print out the Health info
std::cout << "--- UFS Health Descriptor ---\n";
std::cout << "Descriptor ID: 0x" << std::hex << (int)healthData->bDescriptorID << std::dec << "\n";
std::cout << "Pre EOL Info (0x01=Normal, 0x02=Warning, 0x03=Critical): 0x" << std::hex << (int)healthData->bPreEOLInfo << std::dec << "\n";
std::cout << "Device Life Time Est A (SLC): " << (int)healthData->bDeviceLifeTimeEstA * 10 << "%\n";
std::cout << "Device Life Time Est B (MLC/TLC): " << (int)healthData->bDeviceLifeTimeEstB * 10 << "%\n";
CloseHandle(hDevice);
return 0;
}
If the DeviceIoControl fails, check the returned System Error Codes:
-
ERROR_NOT_SUPPORTED(50) orERROR_INVALID_FUNCTION(1): This usually means the request structure is correct, but the underlying storage controller driver doesn't recognize the UFS protocol command. Ensure you are targeting the correctPhysicalDriveXthat corresponds to the UFS device, and that the OEM driver supports pass-through. -
ERROR_INVALID_PARAMETER(87): This implies the buffer size, offset, orPropertyIdwas set up incorrectly. Sometimes switchingStorageAdapterProtocolSpecificPropertytoStorageDeviceProtocolSpecificPropertyworks depending on how the miniport driver handles the IOCTL.
Hope this helps with your question. If you found my response helpful, you could follow this guide to provide feedback.
Thank you.