none
NVMe Passthrough for vendor specific commands returns error 1: ERROR_INVALID_FUNCTION

    Question

  • 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 https://docs.microsoft.com/en-us/windows/desktop/FileIO/working-with-nvme-devices#pass-through-mechanism

    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



    Thursday, May 9, 2019 2:02 PM

All replies

  • Anyone from Microsoft care to comment? its been over 2 weeks and not a single reply :(
    Thursday, May 23, 2019 4:20 PM
  • Hello,

    A dev from the feature team reviewed this and statest that the code looks fine and asked does the vendor specific commands send down has corresponding command effects data from command effects log? If not, storport will block any command to the device as mentioned in https://docs.microsoft.com/en-us/windows/desktop/FileIO/working-with-nvme-devices#pass-through-mechanism

    Important:

    StoreNVMe.sys and Storeport.sys will block any command to a device if it is not described in the Command Effects Log. 

    Monday, June 10, 2019 7:55 PM
    Moderator
  • Hello,

    A dev from the feature team reviewed this and statest that the code looks fine and asked does the vendor specific commands send down has corresponding command effects data from command effects log? If not, storport will block any command to the device as mentioned in https://docs.microsoft.com/en-us/windows/desktop/FileIO/working-with-nvme-devices#pass-through-mechanism

    Important:

    StoreNVMe.sys and Storeport.sys will block any command to a device if it is not described in the Command Effects Log. 

    Hi, as i mentioned before all the commands i'm trying to send have corresponding entries in the command effects log.

    If you look at the top of my post, you will see the command effects log with supported commands printed in the linux environment since i have no idea how to query the same under windows, i don't think its supported.

    In the example code im trying to send the opcode 0xC3 which is 195 in decimal.

    Here is the corresponding command effects log entry from linux printout:

    ACS195   [Unknown                         ] 00000001

    Monday, June 10, 2019 8:10 PM