Windows 10 NVMe Commands

Finally, from Windows 10, NVM Express Admin Command passthrough interface has arrived.

Windows 10 provides two IOCTLs, IOCTL_STORAGE_QUERY_PROPERTY and IOCTL_STORAGE_PROTOCOL_COMMAND. Then you would think that second one would be one-size-fits-all IOCTL, but it’s not. That one can only be used for “Vendor specific” command. Therefore, only three commands in known commands can be used. If you want others, you should use driver specific passthrough interface. I wrote about them in ‘Naraeon NVMe Tools‘ category.

  1. Identify
  2. Get Log Page
  3. Get Features

But there is an issue. Get Features with 07h(Number of Queues) would cause error. In Feb 2017, there is only one driver that correctly get this information. Samsung NVMe driver 2.1. If you have other product and want to get this information, try other methods to passthrough, which provided in ‘Naraeon NVMe Tools‘ category.

Full version source

Delphi
C++ (Identify)
C++ (Get Features)

2 comments on “Windows 10 NVMe Commands

  1. I am trying to Test self test on WDC PC SN720 SDAPNTW-512G-1006 drive (NVMe 1.3 interface is supported) on Windows 10 RS3 OS but DeviceIoControl() method returned 0 and GetLastError() method return error code 87. NVMe version of the drive is 1.3 as per datasheet and “Drive self Test” bit is enabled in Idenitfy structure.

    Can you suggest what could be wrong with the following code. 

    BOOL NVMeTest::NVMeSelfTest(std::string driveHandleStr)
    {
     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;
    HANDLE hPhyDrive = CreateFile(LPCSTR(driveHandleStr.c_str()),
      GENERIC_READ | GENERIC_WRITE,
      FILE_SHARE_READ | FILE_SHARE_WRITE,
      NULL,
      OPEN_EXISTING,
      0,
      NULL);
     if (hPhyDrive == INVALID_HANDLE_VALUE)
     {
      //printf(“Invalid handle!”);
      return FALSE;
     }
     //
     // Allocate buffer for use.
     //
     ULONG testlength = FIELD_OFFSET(STORAGE_PROTOCOL_COMMAND, Command);
     bufferLength = FIELD_OFFSET(STORAGE_PROTOCOL_COMMAND, Command) + STORAGE_PROTOCOL_COMMAND_LENGTH_NVME +
      4096 + sizeof(NVME_ERROR_INFO_LOG);
     buffer = malloc(bufferLength);
     ZeroMemory(buffer, bufferLength);
     if (buffer == NULL) {
      //printf(“DeviceNVMeQueryProtocolDataTest: allocate buffer failed, exit.\n”);
      return FALSE;
     }
     PSTORAGE_PROTOCOL_COMMAND protocolCommand = (PSTORAGE_PROTOCOL_COMMAND)buffer;
     protocolCommand->Version = STORAGE_PROTOCOL_STRUCTURE_VERSION;
     protocolCommand->Length = sizeof(STORAGE_PROTOCOL_COMMAND);
     protocolCommand->ProtocolType = ProtocolTypeNvme;
     protocolCommand->Flags = STORAGE_PROTOCOL_COMMAND_FLAG_ADAPTER_REQUEST;
     protocolCommand->CommandLength = STORAGE_PROTOCOL_COMMAND_LENGTH_NVME;
     protocolCommand->ErrorInfoLength = sizeof(NVME_ERROR_INFO_LOG);
     // protocolCommand->DataFromDeviceTransferLength = 4096;
     protocolCommand->DataToDeviceTransferLength = 4096; // 4096;
     protocolCommand->TimeOutValue = 180;
     protocolCommand->ErrorInfoOffset = FIELD_OFFSET(STORAGE_PROTOCOL_COMMAND, Command) + STORAGE_PROTOCOL_COMMAND_LENGTH_NVME;
     // protocolCommand->DataFromDeviceBufferOffset = protocolCommand->ErrorInfoOffset + protocolCommand->ErrorInfoLength;
     protocolCommand->DataToDeviceBufferOffset = protocolCommand->ErrorInfoOffset + protocolCommand->ErrorInfoLength;
     protocolCommand->CommandSpecific = STORAGE_PROTOCOL_SPECIFIC_NVME_ADMIN_COMMAND;
     PNVME_COMMAND command = (PNVME_COMMAND)protocolCommand->Command;
     command->CDW0.OPC = 0x14;
     //command->NSID = 0x01;
     command->u.GENERAL.CDW10 = 0x01;
     // 
     // Send request down. 
     // 
     result = DeviceIoControl(hPhyDrive,
      IOCTL_STORAGE_PROTOCOL_COMMAND,
      buffer,
      bufferLength,
      buffer,
      bufferLength,
      &returnedLength,
      NULL
     );
     if (result == 0)
     {
      printf(“Error occurred %d\n”, GetLastError()); // Returns 87
      free(buffer);
      return FALSE;
     }
     return TRUE;
    }

Leave a Reply to eric Cancel reply

Your email address will not be published. Required fields are marked *