NVM Passthrough for vendor specific commands returns error 1: ERROR_INVALID_FUNCTION

  • Thread starter Thread starter clearshot365
  • Start date Start date
C

clearshot365

Guest
Hi

I have closely followed the example on how to send a NVME Vendor Specific pass-through commands to the device, however i can't figure out why the ERROR_INVALID_FUNCTION code gets returned every single time.

Im working with an NVME device which is developed in house (our own SSD/NVME controller chip) which has been tested and works under linux with no issues.

I have also verified, that the device is able to send the Command Effects Log with all the commands that is supports, here is the output of this log queried under Linux environment:

fpga@host:[~/WORK/nvme-cli/nvme-cli]: sudo ./nvme effects-log /dev/nvme0
Admin Command Set
ACS0 [Delete I/O Submission Queue ] 00000001
ACS1 [Create I/O Submission Queue ] 00000001
ACS2 [Get Log Page ] 00000001
ACS4 [Delete I/O Completion Queue ] 00000001
ACS5 [Create I/O Completion Queue ] 00000001
ACS6 [Identify ] 00000001
ACS8 [Abort ] 00000001
ACS9 [Set Features ] 00000001
ACS10 [Get Features ] 00000001
ACS12 [Asynchronous Event Request ] 00000001
ACS16 [Firmware Commit ] 00000001
ACS17 [Firmware Image Download ] 00000001
ACS128 [Format NVM ] 00000001
ACS129 [Security Send ] 00000001
ACS130 [Security Receive ] 00000001
ACS192 [Unknown ] 00000001
ACS193 [Unknown ] 00000001
ACS194 [Unknown ] 00000001
ACS195 [Unknown ] 00000001
ACS196 [Unknown ] 00000001
ACS197 [Unknown ] 00000001
ACS198 [Unknown ] 00000001
ACS199 [Unknown ] 00000001
ACS200 [Unknown ] 00000001

NVM Command Set
IOCS0 [Flush ] 00000001
IOCS1 [Write ] 00000001
IOCS2 [Read ] 00000001
IOCS4 [Write Uncorrectable ] 00000001
IOCS5 [Compare ] 00000001
IOCS8 [Write Zeroes ] 00000001
IOCS9 [Dataset Management ] 00000001
IOCS13 [Reservation Register ] 00000001
IOCS14 [Reservation Report ] 00000001
IOCS17 [Reservation Acquire ] 00000001
IOCS21 [Reservation Release ] 00000001
IOCS131 [Unknown ] 00000001
IOCS132 [Unknown ] 00000001
IOCS133 [Unknown ] 00000001
IOCS134 [Unknown ] 00000001


I have based the test code i have for windows on the example from Working with NVMe drives - Windows applications

I'm able to send a standard IDENTITY command from the same example with no issues and getting a proper response.

However, when i try to send a Vendor Specific Admin command with any command code from 0xC1-0xC6 i get the error 1 (ERROR_INVALID_FUNCTION)

Here is the code i use to send a Vendor Specific Admin command:


BOOL nvme_vendor_admin_cmd(HANDLE hDevice)
{

BOOL result = 0;
PVOID buffer = NULL;
ULONG bufferLength = 0;
ULONG returnedLength = 0;
PSTORAGE_PROTOCOL_COMMAND protocolCommand = NULL;
PNVME_COMMAND command = NULL;

//
// Allocate buffer for use.
//

bufferLength = FIELD_OFFSET(STORAGE_PROTOCOL_COMMAND, Command) +
STORAGE_PROTOCOL_COMMAND_LENGTH_NVME + sizeof(NVME_ERROR_INFO_LOG) + NVME_MAX_LOG_SIZE;

buffer = malloc(bufferLength);

if(!buffer)
return FALSE;
//
// Initialize query data structure to get Identify Controller Data.
//
ZeroMemory(buffer, bufferLength);

protocolCommand = (PSTORAGE_PROTOCOL_COMMAND)buffer;
command = (PNVME_COMMAND)protocolCommand->Command;

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 = NVME_MAX_LOG_SIZE;
protocolCommand->TimeOutValue = 10;
protocolCommand->ErrorInfoOffset = FIELD_OFFSET(STORAGE_PROTOCOL_COMMAND, Command) + STORAGE_PROTOCOL_COMMAND_LENGTH_NVME;
protocolCommand->DataToDeviceBufferOffset = protocolCommand->ErrorInfoOffset + protocolCommand->ErrorInfoLength;
protocolCommand->CommandSpecific = STORAGE_PROTOCOL_SPECIFIC_NVME_ADMIN_COMMAND;

command->CDW0.OPC = 0xC3; // opcode;
command->u.GENERAL.CDW10 = 256;
command->u.GENERAL.CDW11 = 0;
command->u.GENERAL.CDW12 = 0;
command->u.GENERAL.CDW13 = 0;
command->u.GENERAL.CDW14 = 0;

//
// Send request down.
//
result = DeviceIoControl(hDevice,
IOCTL_STORAGE_PROTOCOL_COMMAND,
buffer,
bufferLength,
buffer,
bufferLength,
&returnedLength,
NULL
);

if (FALSE == result)
{
DWORD dw = GetLastError();
std::cout << "LastErrorCode: " << dw << "\n";
goto exit;
}
else
{
std::cout << "NVME Vendor Admin Command Success: " << dw << "\n";
}

exit:

if (buffer)
free(buffer);

return result;

}

Please let me know what am i doing wrong since i need to get this to work with our device under windows.

Any help is greatly appreciated

Continue reading...
 
Back
Top