Storport is a welcome relief to storage driver writers wishing to develop a driver that exports a virtual device. This article updates the Virtual Storport Miniport Driver that we developed in the earlier three issues of The NT Insider.
Another Article?
Well to be honest, when we completed the last article we put the Virtual Storport Miniport Driver code away and expected not to have to revisit it for a long while. However, we were contacted by Mike Berhan of busTRACE Technologies who used their busTRACE 9.0 product (www.bustrace.com) to see how accurate our implementation was in emulating a SCSI adapter/device.
So, How Did We Do?
Well, it seems that we missed some stuff. And while windows didn’t complain, we’d be doing the development community a disservice if we didn’t clean the errors up and indicate where those errors were. So the purpose of this article is to tell you the errors that were found and how they were corrected. Of course, we’ll replace the current download code with the updated version.
So let’s get into what Mike found, using busTRACE and busPROBE (which are terrific tools, by the way).
Assumption of Pre-Zeroed Data Buffers
For SCSI operations other than read or write, Mike noted their busPROBE product found that the Storport Virtual Miniport driver did not pre-zero the data buffer which will contain the output from the driver. Thus when the driver filled in the return data, and fields that were not explicitly zeroed were filled with random data..
While this may not seem like a big deal, remember that the Storport Virtual Miniport Driver is trying to create and emulate fully compliant SCSI devices, so in order to be compliant the driver was changed to do this.
No SRB DataTransferLength Update
The biggest violation that busTRACE found was the failure of the Storport Virtual Miniport to update the DataTransferLength field in the SRB when completing a command that involved a data transfer. While this isn’t critical, in order to be properly compliant with the Microsoft’s SRB definition requirement, it must be filled in. Thus, for example, if you diff the old and new code you’ll see that OsrUserReadData and OsrUserWriteData now set the SRB DataTransferLength field to the size of the data transfer.
busTRACE also pointed out that when the driver failed a request, it was not setting the SRB’s DataTransferLength filed to zero to indicate that no data was transferred.
Validating the SRB DataTransferLength
Another interesting busTRACE find was that the Storport Virtual Miniport Driver was not validating that the size of the buffer described by the SRBs’ DataTransferLength field was large enough to hold the data requested. What this means is that when the Storport Virtual Miniport gets a request, for example a read or write, it needs to get the length of the transfer described in the CDB and ensure that the SRB DataTransferLength field is large enough. Failure to make this check would lead to crashes or data corruption since the driver could attempt to access past the end of the allocated buffer.
SCSIOP_INQUIRY – Additional Length Field Not Set
busTRACE also flagged the Storport Virtual Miniport Driver for an error processing the SCSIOP_INQUIRY command, because the Miniport wasn’t filling in the AdditionalLength field of the SCSI_INQUIRY_DATA that we were returning. The field was being left as zero which is invalid. In the code, it should be set to 0x1F (0x1F plus 5 = 0x24 bytes which is the valid length). In the driver the code sets AdditionalLength to “INQUIRYDATABUFFERSIZE-5” where INQUIRYDATABUFFERSIZE is 36 and the “-5” accounts for the 5 bytes of data (this includes that AdditionalLength byte) preceed the additional data returned.
Figure 1 shows the busTRACE output that highlights the error (see Additional Length in the figure). In the retail version of the product, the user can float the mouse over the red highlighted area and busTRACE tells you what firmware bug it has detected
Figure 2 shows how the SCSIOP_INQUIRY processing code was corrected to correct the errors that busTRACEflagged. Notice the setting of the pInquiryData->AdditionalLength field as well as the setting of the PSRB->DataTransferLength field.
case SCSIOP_INQUIRY : {// 0x12 PCDB pCdb = (PCDB) &PSrb->Cdb; PUCHAR pBuffer = (PUCHAR) OsrSpGetSrbDataAddress(pIInfo->OsrSPLocalHandle,PSrb); PINQUIRYDATA pInquiryData; if(!pBuffer || PSrb->DataTransferLength < INQUIRYDATABUFFERSIZE) { status = STATUS_INSUFFICIENT_RESOURCES; //Irp->IoStatus.Information = 0; PSrb->SrbStatus = SRB_STATUS_ERROR; goto completeRequest; } pInquiryData = (PINQUIRYDATA) pBuffer; // // The media is a regular disk, return the correct information. // ASSERT(pIInfo->StorageType == OsrDisk); pInquiryData->DeviceType = DIRECT_ACCESS_DEVICE; pInquiryData->DeviceTypeQualifier = DEVICE_CONNECTED; pInquiryData->DeviceTypeModifier = 0; pInquiryData->RemovableMedia = TRUE; pInquiryData->Versions = 2; // SCSI-2 support pInquiryData->ResponseDataFormat = 2; // Same as Version?? according to SCSI book pInquiryData->Wide32Bit = TRUE; // 32 bit wide transfers pInquiryData->Synchronous = TRUE; // Synchronous commands pInquiryData->CommandQueue = FALSE; // Does not support tagged commands pInquiryData->AdditionalLength = INQUIRYDATABUFFERSIZE-5; // Amount of data we are returning pInquiryData->LinkedCommands = FALSE; // No Linked Commands RtlCopyMemory((PUCHAR) &pInquiryData->VendorId[0],OSR_INQUIRY_VENDOR_ID, strlen(OSR_INQUIRY_VENDOR_ID)); RtlCopyMemory((PUCHAR) &pInquiryData->ProductId[0],OSR_INQUIRY_PRODUCT_ID, strlen(OSR_INQUIRY_PRODUCT_ID)); RtlCopyMemory((PUCHAR) &pInquiryData->ProductRevisionLevel[0],OSR_INQUIRY_PRODUCT_REVISION, strlen(OSR_INQUIRY_PRODUCT_REVISION)); status = STATUS_SUCCESS; PSrb->SrbStatus = SRB_STATUS_SUCCESS; PSrb->DataTransferLength = INQUIRYDATABUFFERSIZE; goto completeRequest; }
Another SCSIOP_INQUIRY issue that busTRACE found was that the driver was not checking the EnableVitalProductData bit (VPD bit) and PAGE CODE field on input. This request, shown in Figure 3, is sent by the Storport driver, after it has enumerated a device in order to enumerate a devices’ VPD pages. The Storport Virtual Miniport Driver completely ignored this bit. Since we saw no reason to support this feature the Storport Virtual Miniport driver was changed to fail the SCSIOP_INQUIRY request if the VPD bit and the PAGE CODE field is zero. This was done to be compliant with the SCSI specifications.
Mode Sense Emulation
As with most of the errors in the Virtual Storport Miniport Driver, the error in handling the Mode Sense command was in setting the Mode Data Length field in the returned data. As Figure 4 illustrates the code was returning 0x4 in the Mode Data Length field of the returned data.
If there are only four bytes actually valid, then the “Mode Data Length” should be set to 3 (i.e. 3 bytes are valid following the Mode Data Length field) since the Mode Data Length field is not included in the count.
Prevent/Allow Medium Handling
When the Storport Virtual Miniport received a SCSI operation that it did not handle, the driver set the SRB status to SRB_STATUS_ERROR and completed the request. While this isn’t necessarily wrong, the Storport Virtual Miniport Driver is trying to emulate a device capable of handling CDBs. Therefore, the Miniport should really be responding as a real conformant device would. What this means is that, if an SRB SenseInfoBuffer has been specified, the driver should be filling it correctly.
Figure 5, shows the routine ProcessScsiCommand Error that sets the sense data that is returned when a command error occurs. This data will be returned if the input SRB contains a SenseInfoBuffer. Of course, your implementation of this may vary depending on what your driver needs to accomplish.
NTSTATUS ProcessScsiCommandError(PSCSI_REQUEST_BLOCK PSrb) { NTSTATUS status = STATUS_SUCCESS; if(PSrb->SenseInfoBuffer && PSrb->SenseInfoBufferLength) { PSENSE_DATA pSense = (PSENSE_DATA) PSrb->SenseInfoBuffer; RtlZeroMemory(pSense,PSrb->SenseInfoBufferLength); pSense->ErrorCode = 0x70; pSense->Valid = 0; pSense->SenseKey = SCSI_SENSE_ILLEGAL_REQUEST; pSense->AdditionalSenseLength = 0x15; pSense->AdditionalSenseCode = SCSI_ADSENSE_ILLEGAL_COMMAND; pSense->AdditionalSenseCodeQualifier = 0; PSrb->ScsiStatus = SCSISTAT_CHECK_CONDITION; PSrb->SrbStatus = SRB_STATUS_AUTOSENSE_VALID | SRB_STATUS_ERROR; } else { status = STATUS_UNSUCCESSFUL; PSrb->SrbStatus = SRB_STATUS_ERROR; } return status; }
Notice that the driver “OR’d” SRB_STATUS_AUTOSENSE _VALID to SRB_STATUS_ERROR to indicate to the caller that the SenseInfoBuffer contains valid data.
When you look at the new code, you will see that the driver has been modified to call the routine in Figure 5 whenever it receives a command that it does not handle.
Adapter Type
One interesting thing that Mike pointed out was that the AdapterInterfaceType for the Virtual Storport Driver was showing up as “Fibre”. This AdapterInterfaceType is a field in the PORT_CONFIGURATION_ INFORMATION block that is set up in our HwFindAdapter routine. Figuring that the field was not initialized the driver was modified to set the field to PNPBus. To our surprise setting this field had no effect. Since we were stumped as to why, we contacted James Antognini of Microsoft WDK support who informed us that Storport gets the setting from the field “\Registry\Machine\System\Current ControlSet\Services\<driver name>\Parmeters\BusType”. This field is a 32-bit REG_DWORD. Thus we modified the INF file to set the value of this key to 0xE (BusTypeVirtual) and it worked. If you don’t set a value, it defaults to BusTypeFibre.
Crash
Alas, during his testing Mike managed to get the driver to crash. This problem has been fixed in the new code drop. It turns out that the code in CreateConnection that added a new connection to the list of current connections did not synchronize access to the ConnectionList. Thus if you added and removed connections quickly enough you would cause the list to become corrupted. The fix was simple; just hold the ConnectionListLock around the adding of the new entry to the list in “CreateConnection”.
Summary
In our article series Writing a Virtual Storport Miniport Driver we described key aspects of the architecture, design, and implementation of this type of driver. We’ve built on that series, and made the driver more compliant, with the tweaks described in this article. And while these tweaks might not be absolutely critical, they do make the driver more compliant with the SCSI specification and thus make it act more properly like a physical device. This compliance helps increase the driver’s chance of interoperating with all sorts of software, and that’s a certainly good thing.
Download sample code to OSR’s Virtual Storport Miniport Driver
OSR would like to thank Mike Berhan of busTRACE Technologies for taking the time to test and analyze the OSR Virtual Storport Miniport Driver and for providing us with a copy of busTRACE so that we could find and fix the issues discussed in this article. Without the help of Mike and his fine tool, we wouldn’t have known about the driver’s failure to conform to the specifications.