Sample Code
Windows Driver Samples/ StorAhci StorPort Miniport Driver/ C++/ src/ common.c/
/*++ Copyright (C) Microsoft Corporation, 2009 Module Name: common.c Abstract: This file contains common ATA related functions. Notes: Revision History: --*/ #if _MSC_VER >= 1200 #pragma warning(push) #endif #pragma warning(disable:4214) // bit field types other than int #pragma warning(disable:4201) // nameless struct/union #pragma warning(disable:26015) //26015: "Potential overflow" #include "generic.h" // // This structure is used as a wrapper of a MODE_PARAMETER_HEADER // or MODE_PARAMETER_HEADER10. Code that uses this structure can be // agnostic as to the underlying structure. // typedef struct _MODE_PARAMETER_HEADER_WRAPPER { // // Pointer to the actual MODE_PARAMETER_HEADER/10. // PVOID ModePageHeader; // // The size in bytes of the structure pointed to by ModePageHeader. // ULONG HeaderSize; // // A pointer to the DeviceSpecificParameter field of ModePageHeader. // PUCHAR DeviceSpecificParameter; // // A pointer to the ModeDataLength field of ModePageHeader. If ModePageHeader // is a pointer to MODE_PARAMETER_HEADER10, then this field points to the least // significant byte of ModePageHeader's ModeDataLength field. // PUCHAR ModeDataLength; } MODE_PARAMETER_HEADER_WRAPPER, *PMODE_PARAMETER_HEADER_WRAPPER; VOID ULong2HexString ( _In_ ULONG Number, _Inout_updates_bytes_ (Length) PUCHAR StringBuffer, _In_ ULONG Length ); VOID BuildHybridEvictCommand( _Inout_ PAHCI_H2D_REGISTER_FIS CFIS, _In_ USHORT BlockCount ); ULONG SCSItoATA( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ PSTORAGE_REQUEST_BLOCK Srb ) /* Note: If there is a need to send a command to device, the translation routine shall set appropriate value to srbExtension->AtaFunction. For example: srbExtension->AtaFunction = ATA_FUNCTION_ATA_COMMAND; Not setting this field can cause the Srb being completed earlier than expected. */ { ULONG status; ULONG cdbLength = 0; PCDB cdb = RequestGetSrbScsiData(Srb, &cdbLength, NULL, NULL, NULL); if (cdb != NULL) { if (IsAtapiDevice(&ChannelExtension->DeviceExtension->DeviceParameters)) { // Atapi command status = SrbConvertToATAPICommand(ChannelExtension, Srb, cdb); } else if (IsAtaDevice(&ChannelExtension->DeviceExtension->DeviceParameters) || (cdb->CDB10.OperationCode == SCSIOP_REPORT_LUNS) || (cdb->CDB10.OperationCode == SCSIOP_INQUIRY) ) { // Ata command, or device enumeration commands. status = SrbConvertToATACommand(ChannelExtension, Srb, cdb, cdbLength); } else { Srb->SrbStatus = SRB_STATUS_NO_DEVICE; status = STOR_STATUS_INVALID_DEVICE_REQUEST; } } else { Srb->SrbStatus = SRB_STATUS_BAD_FUNCTION; status = STOR_STATUS_INVALID_PARAMETER; } return status; } ULONG SrbConvertToATAPICommand( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ PSTORAGE_REQUEST_BLOCK Srb, _In_ PCDB Cdb ) /* Note: If there is a need to send a command to device, the translation routine shall set appropriate value to srbExtension->AtaFunction. For example: srbExtension->AtaFunction = ATA_FUNCTION_ATAPI_COMMAND; Not setting this field can cause the Srb being completed earlier than expected. */ { ULONG status; switch (Cdb->CDB10.OperationCode) { case SCSIOP_MODE_SENSE: if (IsTapeDevice(ChannelExtension->DeviceExtension)) { // tape drive specific. status = AtapiCommonRequest(ChannelExtension, Srb, Cdb); } else { // specail process for 6 bytes command, storahci always send down 10 bytes command to device status = AtapiModeSenseRequest(ChannelExtension, Srb, Cdb); } break; case SCSIOP_MODE_SELECT: if (IsTapeDevice(ChannelExtension->DeviceExtension)) { // tape drive specific. status = AtapiCommonRequest(ChannelExtension, Srb, Cdb); } else { // specail process for 6 bytes command, storahci always send down 10 bytes command to device status = AtapiModeSelectRequest(ChannelExtension, Srb, Cdb); } break; case SCSIOP_INQUIRY: status = AtapiInquiryRequest(ChannelExtension, Srb); break; case SCSIOP_REPORT_LUNS: //use cached info for this command. status = AtaReportLunsCommand(ChannelExtension, (PVOID)Srb); break; case SCSIOP_ATA_PASSTHROUGH16: status = AtaPassThroughRequest(ChannelExtension, Srb, Cdb); break; default: status = AtapiCommonRequest(ChannelExtension, Srb, Cdb); break; } return status; } ULONG AtapiCommonRequest ( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ PSTORAGE_REQUEST_BLOCK Srb, _In_ PCDB Cdb ) /*++ Routine Description: --*/ { PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(Srb); ULONG srbFlags = SrbGetSrbFlags(Srb); UNREFERENCED_PARAMETER(ChannelExtension); srbExtension->AtaFunction = ATA_FUNCTION_ATAPI_COMMAND; if (srbFlags & SRB_FLAGS_DATA_IN) { srbExtension->Flags |= ATA_FLAGS_DATA_IN; } if (srbFlags & SRB_FLAGS_DATA_OUT) { srbExtension->Flags |= ATA_FLAGS_DATA_OUT; } // set the transfer mode to be used if ( DmaSafeAtapiCommand(Cdb->CDB10.OperationCode) ) { srbExtension->Flags |= ATA_FLAGS_USE_DMA; } return STOR_STATUS_SUCCESS; } VOID AtapiInquiryCompletion ( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ PSTORAGE_REQUEST_BLOCK Srb ) { STOR_UNIT_ATTRIBUTES attributes = {0}; UCHAR pathId = 0; UCHAR targetId = 0; UCHAR lun = 0; if (Srb->SrbStatus != SRB_STATUS_SUCCESS) { return; } SrbGetPathTargetLun(Srb, &pathId, &targetId, &lun); StorPortSetDeviceQueueDepth(ChannelExtension->AdapterExtension, pathId, targetId, lun, ChannelExtension->MaxPortQueueDepth); if (IsAtapiDevice(&ChannelExtension->DeviceExtension->DeviceParameters)) { attributes.DeviceAttentionSupported = ChannelExtension->DeviceExtension[0].IdentifyPacketData->SerialAtaCapabilities.SlimlineDeviceAttention; attributes.AsyncNotificationSupported = IsDeviceSupportsAN(ChannelExtension->DeviceExtension->IdentifyPacketData); attributes.D3ColdNotSupported = (ChannelExtension->AdapterExtension->StateFlags.SupportsAcpiDSM == 0); StorPortSetUnitAttributes(ChannelExtension->AdapterExtension, (PSTOR_ADDRESS)&ChannelExtension->DeviceExtension[0].DeviceAddress, attributes); } return; } ULONG AtapiInquiryRequest ( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ PSTORAGE_REQUEST_BLOCK Srb ) /*++ Routine Description: --*/ { PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(Srb); UNREFERENCED_PARAMETER(ChannelExtension); srbExtension->AtaFunction = ATA_FUNCTION_ATAPI_COMMAND; srbExtension->CompletionRoutine = AtapiInquiryCompletion; srbExtension->Flags |= ATA_FLAGS_DATA_IN; return STOR_STATUS_SUCCESS; } VOID AtapiModeCommandRequestCompletion ( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ PSTORAGE_REQUEST_BLOCK Srb ) /*++ Description Used as completion routine for: AtapiModeSenseRequest and AtapiModeSelectRequest Arguments: Return value: --*/ { PVOID dataBuffer = SrbGetDataBuffer(Srb); UCHAR bytesAdjust = sizeof(MODE_PARAMETER_HEADER10) - sizeof(MODE_PARAMETER_HEADER); PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(Srb); if (Srb->SrbStatus == SRB_STATUS_BUS_RESET) { if (srbExtension->DataBuffer != NULL) { AhciFreeDmaBuffer(ChannelExtension->AdapterExtension, (ULONG_PTR)srbExtension->CompletionContext, srbExtension->DataBuffer); srbExtension->DataBuffer = NULL; } return; } if ((dataBuffer == NULL) || (srbExtension->DataBuffer == NULL) || (SrbGetDataTransferLength(Srb) < sizeof(MODE_PARAMETER_HEADER)) || (srbExtension->DataTransferLength < sizeof(MODE_PARAMETER_HEADER)) ) { // free the memory and mark the Srb with error status. if (srbExtension->DataBuffer != NULL) { AhciFreeDmaBuffer(ChannelExtension->AdapterExtension, (ULONG_PTR)srbExtension->CompletionContext, srbExtension->DataBuffer); srbExtension->DataBuffer = NULL; } Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; return; } if (srbExtension->Cdb.CDB10.OperationCode == SCSIOP_MODE_SENSE10) { // if it's completion routine for MODE SENSE, copy the data back to original buffer PUCHAR originalBuffer; PUCHAR currentBuffer; PMODE_PARAMETER_HEADER header; PMODE_PARAMETER_HEADER10 header10; ULONG transferLength = RequestGetDataTransferLength(Srb); originalBuffer = (PUCHAR)dataBuffer; currentBuffer = (PUCHAR)srbExtension->DataBuffer; NT_ASSERT(originalBuffer != NULL); NT_ASSERT(currentBuffer != NULL); header = (PMODE_PARAMETER_HEADER)originalBuffer; header10 = (PMODE_PARAMETER_HEADER10)currentBuffer; // Mode parameter header 10 and mode parameter header 6 differ by 3 bytes header->ModeDataLength = header10->ModeDataLength[1] - 3; header->MediumType = header10->MediumType; // ATAPI Mode Parameter Header doesn't have these fields. header->DeviceSpecificParameter = header10->DeviceSpecificParameter; header->BlockDescriptorLength = header10->BlockDescriptorLength[1]; // copy the rest of the data if (transferLength > sizeof(MODE_PARAMETER_HEADER10)) { StorPortCopyMemory(originalBuffer+sizeof(MODE_PARAMETER_HEADER), currentBuffer+sizeof(MODE_PARAMETER_HEADER10), min(SrbGetDataTransferLength(Srb) - sizeof(MODE_PARAMETER_HEADER), transferLength - sizeof(MODE_PARAMETER_HEADER10))); } // adjust data transfer length. if (transferLength > bytesAdjust) { SrbSetDataTransferLength(Srb, transferLength - bytesAdjust); } else { // error. transfer length should be zero. // if it is less than the header, we will just pass it up. SrbSetDataTransferLength(Srb, transferLength); } } if (srbExtension->DataBuffer != NULL) { AhciFreeDmaBuffer(ChannelExtension->AdapterExtension, (ULONG_PTR)srbExtension->CompletionContext, srbExtension->DataBuffer); srbExtension->DataBuffer = NULL; } return; } ULONG AtapiModeSenseRequest ( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ PSTORAGE_REQUEST_BLOCK Srb, _In_ PCDB Cdb ) /*++ --*/ { ULONG status; ULONG modeSenseBufferSize; ULONG tempLength; PMODE_PARAMETER_HEADER10 modeSenseBuffer; STOR_PHYSICAL_ADDRESS modeSensePhysialAddress; PUCHAR origBuffer; UCHAR bytesAdjust; PCDB cdb; USHORT allocationLength; PMODE_PARAMETER_HEADER header; PMODE_PARAMETER_HEADER10 header10; PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(Srb); PVOID srbDataBuffer = SrbGetDataBuffer(Srb); ULONG srbDataBufferLength = SrbGetDataTransferLength(Srb); status = STOR_STATUS_SUCCESS; modeSenseBuffer = NULL; modeSenseBufferSize = 0; bytesAdjust = sizeof(MODE_PARAMETER_HEADER10) - sizeof(MODE_PARAMETER_HEADER); // Validate input length if (srbDataBufferLength < sizeof(MODE_PARAMETER_HEADER)) { Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; status = STOR_STATUS_BUFFER_TOO_SMALL; goto Done; } if (srbDataBuffer == NULL) { Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; status = STOR_STATUS_INVALID_PARAMETER; goto Done; } modeSenseBufferSize = srbDataBufferLength + bytesAdjust; origBuffer = (PUCHAR)srbDataBuffer; if (modeSenseBufferSize < sizeof(MODE_PARAMETER_HEADER10) ) { Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; status = STOR_STATUS_BUFFER_TOO_SMALL; goto Done; } // We need to allocate a new data buffer since the size is different status = AhciAllocateDmaBuffer(ChannelExtension->AdapterExtension, modeSenseBufferSize, (PVOID*)&modeSenseBuffer); if ( (status != STOR_STATUS_SUCCESS) || (modeSenseBuffer == NULL) ) { // memory allocation failed Srb->SrbStatus = SRB_STATUS_ERROR; status = STOR_STATUS_INSUFFICIENT_RESOURCES; goto Done; } AhciZeroMemory((PCHAR)modeSenseBuffer, modeSenseBufferSize); modeSensePhysialAddress = StorPortGetPhysicalAddress(ChannelExtension->AdapterExtension, NULL, (PVOID)modeSenseBuffer, &tempLength); // filll information in the new MODE_SENSE10 header allocationLength = Cdb->MODE_SENSE.AllocationLength; header = (PMODE_PARAMETER_HEADER)origBuffer; header10 = (PMODE_PARAMETER_HEADER10)modeSenseBuffer; header10->ModeDataLength[1] = header->ModeDataLength; header10->MediumType = header->MediumType; header10->BlockDescriptorLength[1] = header->BlockDescriptorLength; allocationLength += bytesAdjust; // set up cdb of MODE_SENSE10 cdb = (PCDB)&srbExtension->Cdb; cdb->MODE_SENSE10.OperationCode = SCSIOP_MODE_SENSE10; cdb->MODE_SENSE10.Dbd = Cdb->MODE_SENSE.Dbd; cdb->MODE_SENSE10.PageCode = Cdb->MODE_SENSE.PageCode; cdb->MODE_SENSE10.Pc = Cdb->MODE_SENSE.Pc; cdb->MODE_SENSE10.AllocationLength[0] = (UCHAR) (allocationLength >> 8); cdb->MODE_SENSE10.AllocationLength[1] = (UCHAR) (allocationLength & 0xff); cdb->MODE_SENSE10.Control = Cdb->MODE_SENSE.Control; // fill in the srbExtension fields srbExtension->AtaFunction = ATA_FUNCTION_ATAPI_COMMAND; srbExtension->Flags |= ATA_FLAGS_NEW_CDB; srbExtension->Flags |= ATA_FLAGS_DATA_IN; srbExtension->DataBuffer = modeSenseBuffer; srbExtension->DataTransferLength = modeSenseBufferSize; srbExtension->CompletionRoutine = AtapiModeCommandRequestCompletion; srbExtension->CompletionContext = (PVOID)modeSenseBufferSize; // preserve the buffer size, it's needed for freeing the memory srbExtension->LocalSgl.NumberOfElements = 1; srbExtension->LocalSgl.List[0].PhysicalAddress.LowPart = modeSensePhysialAddress.LowPart; srbExtension->LocalSgl.List[0].PhysicalAddress.HighPart = modeSensePhysialAddress.HighPart; srbExtension->LocalSgl.List[0].Length = modeSenseBufferSize; srbExtension->Sgl = &srbExtension->LocalSgl; Done: if (status != STOR_STATUS_SUCCESS) { if (modeSenseBuffer != NULL) { AhciFreeDmaBuffer(ChannelExtension->AdapterExtension, modeSenseBufferSize, modeSenseBuffer); } srbExtension->DataBuffer = NULL; } return status; } ULONG AtapiModeSelectRequest ( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ PSTORAGE_REQUEST_BLOCK Srb, _In_ PCDB Cdb ) /*++ return value: STOR_STATUS --*/ { ULONG status; PVOID modeSelectBuffer; ULONG modeSelectBufferSize; ULONG tempLength; STOR_PHYSICAL_ADDRESS modeSelectPhysialAddress; UCHAR bytesToSkip; PUCHAR origBuffer; UCHAR bytesAdjust; PCDB cdb; USHORT paramListLength; PMODE_PARAMETER_HEADER header; PMODE_PARAMETER_HEADER10 header10; PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(Srb); PVOID srbDataBuffer = SrbGetDataBuffer(Srb); ULONG srbDataBufferLength = SrbGetDataTransferLength(Srb); status = STOR_STATUS_SUCCESS; modeSelectBuffer = NULL; modeSelectBufferSize = 0; origBuffer = (PUCHAR)srbDataBuffer; header = (PMODE_PARAMETER_HEADER)origBuffer; if (srbDataBuffer == NULL) { Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; status = STOR_STATUS_INVALID_PARAMETER; goto Done; } // Validate input length if ( (srbDataBufferLength < sizeof(MODE_PARAMETER_HEADER)) || (srbDataBufferLength < (sizeof(MODE_PARAMETER_HEADER) + header->BlockDescriptorLength)) ) { Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; status = STOR_STATUS_BUFFER_TOO_SMALL; goto Done; } // Check for invalid BlockDescriptorLength if ( (sizeof(MODE_PARAMETER_HEADER) + header->BlockDescriptorLength) > 0xFF ) { Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; status = STOR_STATUS_INVALID_PARAMETER; goto Done; } // Atapi devices don't use the block descriptor. bytesAdjust = sizeof(MODE_PARAMETER_HEADER10) - sizeof(MODE_PARAMETER_HEADER); modeSelectBufferSize = srbDataBufferLength + bytesAdjust - header->BlockDescriptorLength; // allocate buffer for the new cdb status = AhciAllocateDmaBuffer(ChannelExtension->AdapterExtension, modeSelectBufferSize, (PVOID*)&modeSelectBuffer); if ( (status != STOR_STATUS_SUCCESS) || (modeSelectBuffer == NULL) ) { // Srb->SrbStatus = SRB_STATUS_ERROR; status = STOR_STATUS_INSUFFICIENT_RESOURCES; goto Done; } AhciZeroMemory((PCHAR)modeSelectBuffer, modeSelectBufferSize); modeSelectPhysialAddress = StorPortGetPhysicalAddress(ChannelExtension->AdapterExtension, NULL, (PVOID)modeSelectBuffer, &tempLength); header10 = (PMODE_PARAMETER_HEADER10)modeSelectBuffer; header10->ModeDataLength[1] = header->ModeDataLength; header10->MediumType = header->MediumType; // block descriptor length in header10 should be 0 for ATAPI devices // do not copy the block descriptor. Atapi devices don't use the block descriptor. bytesToSkip = sizeof(MODE_PARAMETER_HEADER) + header->BlockDescriptorLength; // Copy any remaining buffer contents if (srbDataBufferLength > bytesToSkip) { StorPortCopyMemory( ((PUCHAR)modeSelectBuffer + sizeof(MODE_PARAMETER_HEADER10)), ((PUCHAR)origBuffer + bytesToSkip), (srbDataBufferLength - bytesToSkip) ); } paramListLength = Cdb->MODE_SELECT.ParameterListLength; paramListLength += sizeof(MODE_PARAMETER_HEADER10); // Check for integer overflow if (paramListLength < sizeof(MODE_PARAMETER_HEADER10)) { Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; status = STOR_STATUS_INVALID_PARAMETER; goto Done; } // Invalid paramListLength in the original cdb if (paramListLength < bytesToSkip) { Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; status = STOR_STATUS_INVALID_PARAMETER; goto Done; } paramListLength -= bytesToSkip; // fill in the srbExtension fields srbExtension->AtaFunction = ATA_FUNCTION_ATAPI_COMMAND; srbExtension->Flags |= ATA_FLAGS_NEW_CDB; srbExtension->Flags |= ATA_FLAGS_DATA_OUT; srbExtension->DataBuffer = modeSelectBuffer; srbExtension->DataTransferLength = modeSelectBufferSize; srbExtension->CompletionRoutine = AtapiModeCommandRequestCompletion; srbExtension->CompletionContext = (PVOID)modeSelectBufferSize; // preserve the buffer size, it's needed for freeing the memory cdb = (PCDB)&srbExtension->Cdb; // fill in the cdb cdb->MODE_SELECT10.OperationCode = SCSIOP_MODE_SELECT10; // ATAPI device supports MODE SELECT 10 cdb->MODE_SELECT10.SPBit = Cdb->MODE_SELECT.SPBit; cdb->MODE_SELECT10.PFBit = 1; cdb->MODE_SELECT10.LogicalUnitNumber = Cdb->MODE_SELECT.LogicalUnitNumber; cdb->MODE_SELECT10.ParameterListLength[0] = (UCHAR)(paramListLength >> 8); cdb->MODE_SELECT10.ParameterListLength[1] = (UCHAR)(paramListLength & 0xff); cdb->MODE_SELECT10.Control = Cdb->MODE_SELECT.Control; srbExtension->LocalSgl.NumberOfElements = 1; srbExtension->LocalSgl.List[0].PhysicalAddress.LowPart = modeSelectPhysialAddress.LowPart; srbExtension->LocalSgl.List[0].PhysicalAddress.HighPart = modeSelectPhysialAddress.HighPart; srbExtension->LocalSgl.List[0].Length = modeSelectBufferSize; // preserve the buffer size, it's needed for freeing the memory srbExtension->Sgl = &srbExtension->LocalSgl; Done: if (status != STOR_STATUS_SUCCESS) { if (modeSelectBuffer != NULL) { AhciFreeDmaBuffer(ChannelExtension->AdapterExtension, modeSelectBufferSize, modeSelectBuffer); } srbExtension->DataBuffer = NULL; } return status; } ULONG SrbConvertToATACommand( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ PSTORAGE_REQUEST_BLOCK Srb, _In_ PCDB Cdb, _In_ ULONG CdbLength ) /* Srb to a ATA device must be translated to an ATA command Note: If there is a need to send a command to device, the translation routine shall set appropriate value to srbExtension->AtaFunction. For example: srbExtension->AtaFunction = ATA_FUNCTION_ATA_COMMAND; Not setting this field can cause the Srb being completed earlier than expected. */ { ULONG status; // translate the cdb to task file register values switch (Cdb->CDB10.OperationCode) { case SCSIOP_READ: case SCSIOP_WRITE: case SCSIOP_READ16: case SCSIOP_WRITE16: status = AtaReadWriteRequest(ChannelExtension, Srb, Cdb, CdbLength); break; case SCSIOP_VERIFY: case SCSIOP_VERIFY16: status = AtaVerifyRequest(ChannelExtension, Srb, Cdb, CdbLength); break; case SCSIOP_MODE_SENSE: case SCSIOP_MODE_SENSE10: status = AtaModeSenseRequest(ChannelExtension, Srb, Cdb); break; case SCSIOP_MODE_SELECT: case SCSIOP_MODE_SELECT10: status = AtaModeSelectRequest(ChannelExtension, Srb, Cdb); break; case SCSIOP_READ_CAPACITY: case SCSIOP_READ_CAPACITY16: status = AtaReadCapacityRequest(ChannelExtension, Srb, Cdb, CdbLength); break; case SCSIOP_INQUIRY: status = AtaInquiryRequest(ChannelExtension, Srb, Cdb); break; case SCSIOP_REPORT_LUNS: status = AtaReportLunsCommand(ChannelExtension, (PVOID)Srb); break; case SCSIOP_START_STOP_UNIT: status = AtaStartStopUnitRequest(ChannelExtension, Srb, Cdb); break; case SCSIOP_TEST_UNIT_READY: status = AtaTestUnitReadyRequest(ChannelExtension, Srb); break; case SCSIOP_MEDIUM_REMOVAL: status = AtaMediumRemovalRequest(ChannelExtension, Srb, Cdb); break; case SCSIOP_SYNCHRONIZE_CACHE: status = AtaFlushCommandRequest(ChannelExtension, Srb); break; case SCSIOP_SECURITY_PROTOCOL_IN: case SCSIOP_SECURITY_PROTOCOL_OUT: // Use a common function as SECURITY_PROTOCOL_IN and SECURITY_PROTOCOL_OUT structures are same. status = AtaSecurityProtocolRequest(ChannelExtension, Srb, Cdb); break; case SCSIOP_ATA_PASSTHROUGH16: status = AtaPassThroughRequest(ChannelExtension, Srb, Cdb); break; case SCSIOP_UNMAP: status = AtaUnmapRequest(ChannelExtension, Srb); break; default: Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; status = STOR_STATUS_INVALID_DEVICE_REQUEST; break; } return status; } VOID AtaSetTaskFileDataRange( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ PSTORAGE_REQUEST_BLOCK Srb, _In_ ULONG64 StartingLba, _In_ ULONG NumberOfBlocks ) /*++ Routine Description: Sets up the following registers in the task file: sectorCount sectorNum CylLow CylHigh deviceHead Arguments: StartingLba NumberOfBlocks Return Value: None. --*/ { LARGE_INTEGER startingSector; PATAREGISTERS previousReg; PATAREGISTERS currentReg; ULONG sectorCount; PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(Srb); startingSector.QuadPart = StartingLba; sectorCount = NumberOfBlocks; NT_ASSERT(sectorCount != 0); NT_ASSERT(sectorCount <= 0x10000); previousReg = &srbExtension->TaskFile.Previous; currentReg = &srbExtension->TaskFile.Current; if (Support48Bit(&ChannelExtension->DeviceExtension->DeviceParameters)) { // load the higher order bytes - PreviousReg SetSectorCount(previousReg, (UCHAR)((sectorCount & 0x0000ff00) >> 8)); SetSectorNumber(previousReg, (UCHAR)(((startingSector.LowPart) & 0xff000000) >> 24)); SetCylinderLow(previousReg, (UCHAR)(((startingSector.HighPart) & 0x000000ff) >> 0)); SetCylinderHigh(previousReg, (UCHAR)(((startingSector.HighPart) & 0x0000ff00) >> 8)); // setup the device/head register. SetDeviceReg(currentReg, IDE_LBA_MODE); } else { // we cannot handle more than 256 sectors NT_ASSERT(sectorCount <= 0x100); // The starting LBA should be less than 28 bits wide NT_ASSERT(startingSector.HighPart == 0); // The upper layers could still send us sectors beyond the // end of the disk. Do not assert. Let the device fail it. // //NT_ASSERT((startingSector.LowPart & 0xF0000000) == 0); // setup the device/head register. SetDeviceReg(currentReg, (UCHAR)(IDE_LBA_MODE | (((startingSector.LowPart) & 0x0f000000) >> 24))); } // load the lower order bytes - CurrentReg SetSectorCount(currentReg, (UCHAR)((sectorCount & 0x000000ff) >> 0)); SetSectorNumber(currentReg, (UCHAR)(((startingSector.LowPart) & 0x000000ff) >> 0)); SetCylinderLow(currentReg, (UCHAR)(((startingSector.LowPart) & 0x0000ff00) >> 8)); SetCylinderHigh(currentReg, (UCHAR)(((startingSector.LowPart) & 0x00ff0000) >> 16)); return; } VOID HybridWriteThroughEvictCompletion( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ PSTORAGE_REQUEST_BLOCK Srb ) { PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(Srb); // // Free DMA buffer that allocated for EVICT command. // AhciFreeDmaBuffer(ChannelExtension->AdapterExtension, ATA_BLOCK_SIZE, srbExtension->DataBuffer); return; } VOID HybridWriteThroughCompletion( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ PSTORAGE_REQUEST_BLOCK Srb ) /*++ Routine Description: WRITE command is completed; Issue EVICT command to evict data out of caching medium. Arguments: ChannelExtension Srb Return Value: None --*/ { ULONG status; ULONG i; PATA_LBA_RANGE buffer = NULL; // DMA buffer allocated for EVICT command STOR_PHYSICAL_ADDRESS bufferPhysicalAddress; PAHCI_SRB_EXTENSION srbExtension; if (Srb->SrbStatus != SRB_STATUS_SUCCESS) { // // WRITE command failed, return to complete the request. // return; } srbExtension = GetSrbExtension(Srb); // // allocate DMA buffer, this buffer will be used to store the ATA LBA Range for EVICT command // status = AhciAllocateDmaBuffer((PVOID)ChannelExtension->AdapterExtension, ATA_BLOCK_SIZE, (PVOID*)&buffer); if ( (status != STOR_STATUS_SUCCESS) || (buffer == NULL) ) { Srb->SrbStatus = SRB_STATUS_ERROR; return; } // // Fill LBA Range into buffer for EVICT command. // AhciZeroMemory((PCHAR)buffer, ATA_BLOCK_SIZE); buffer->StartSector = ((ULONGLONG)srbExtension->Cfis.LBA7_0) | ((ULONGLONG)srbExtension->Cfis.LBA15_8 << 8) | ((ULONGLONG)srbExtension->Cfis.LBA23_16 << 16) | ((ULONGLONG)srbExtension->Cfis.LBA31_24 << 24) | ((ULONGLONG)srbExtension->Cfis.LBA39_32 << 32) | ((ULONGLONG)srbExtension->Cfis.LBA47_40 << 40); buffer->SectorCount = ((ULONGLONG)srbExtension->Cfis.Feature7_0) | ((ULONGLONG)srbExtension->Cfis.Feature15_8 << 8) ; // // save values before calling HybridEvictCompletion() // srbExtension->AtaFunction = ATA_FUNCTION_ATA_CFIS_PAYLOAD; srbExtension->Flags |= ATA_FLAGS_DATA_OUT; srbExtension->DataBuffer = buffer; srbExtension->DataTransferLength = ATA_BLOCK_SIZE; bufferPhysicalAddress = StorPortGetPhysicalAddress(ChannelExtension->AdapterExtension, NULL, buffer, &i); srbExtension->LocalSgl.NumberOfElements = 1; srbExtension->LocalSgl.List[0].PhysicalAddress.LowPart = bufferPhysicalAddress.LowPart; srbExtension->LocalSgl.List[0].PhysicalAddress.HighPart = bufferPhysicalAddress.HighPart; srbExtension->LocalSgl.List[0].Length = ATA_BLOCK_SIZE; srbExtension->Sgl = &srbExtension->LocalSgl; // // Clear the CFIS structure before reuse it. // AhciZeroMemory((PCHAR)&srbExtension->Cfis, sizeof(AHCI_H2D_REGISTER_FIS)); BuildHybridEvictCommand(&srbExtension->Cfis, 1); srbExtension->CompletionRoutine = HybridWriteThroughEvictCompletion; return; } __inline UCHAR GetReadWriteRequestAtaCommand ( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ PSTORAGE_REQUEST_BLOCK Srb, _In_ PCDB Cdb ) /*++ Routine Description: Arguments: ChannelExtension - Srb - Cdb - Return Value: The translated ATA command. --*/ { PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(Srb); UCHAR commandReg = IDE_COMMAND_NOT_VALID; if (IsSupportedReadCdb(Cdb)) { if( ChannelExtension->StateFlags.NCQ_Activated ) { // use NCQ command as preferred command. commandReg = IDE_COMMAND_READ_FPDMA_QUEUED; } else { // SATA device, use DMA read command. if (Is48BitCommand(srbExtension->Flags)) { commandReg = IDE_COMMAND_READ_DMA_EXT; } else { commandReg = IDE_COMMAND_READ_DMA; } } } else { if( ChannelExtension->StateFlags.NCQ_Activated ) { // change the command to be NCQ command commandReg = IDE_COMMAND_WRITE_FPDMA_QUEUED; } else { // Make all writes into WRITE_DMA // If FUA, keep track of FUA even there is nothing to do with this bit later if (Is48BitCommand(srbExtension->Flags)) { if ( (Cdb->CDB10.ForceUnitAccess == 1) && IsFuaSupported(ChannelExtension) ) { // DMA write ext FUA (48 bit) commandReg = IDE_COMMAND_WRITE_DMA_FUA_EXT; } else { // DMA write ext (48 bit) commandReg = IDE_COMMAND_WRITE_DMA_EXT; } } else { // DMA write commandReg = IDE_COMMAND_WRITE_DMA; } } } return commandReg; } VOID BuildReadWriteCommand ( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ PSTORAGE_REQUEST_BLOCK Srb, _In_ PCDB Cdb, _In_ ULONG CdbLength ) /*++ Routine Description: Sets up SATA CFIS data structure for read/write. The routine shall not modify any other field in the srb or device extension. Arguments: ChannelExtension Srb Cdb CdbLength Return Value: None. Notes: All the other fields in the srb should be set before calling this routine. --*/ { LARGE_INTEGER startingSector; ULONG bytesPerSector; ULONG sectorCount; BOOLEAN hybridPriorityPassedIn = FALSE; PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(Srb); ULONG srbDataBufferLength = SrbGetDataTransferLength(Srb); PAHCI_H2D_REGISTER_FIS cfis = &srbExtension->Cfis; bytesPerSector = BytesPerLogicalSector(&ChannelExtension->DeviceExtension->DeviceParameters); // // Calculate sector count. Round up to next block. // sectorCount = (srbDataBufferLength + bytesPerSector - 1) / bytesPerSector; // // Get starting sector number from CDB. // startingSector.QuadPart = GetLbaFromCdb(Cdb, CdbLength); NT_ASSERT(sectorCount != 0); NT_ASSERT(sectorCount <= 0x10000); // transfer length should not over ATA physical transfer limitation. // // Command and AtaFunction should be set first. It will be referenced later in this function. // srbExtension->AtaFunction = ATA_FUNCTION_ATA_CFIS_PAYLOAD; cfis->Command = GetReadWriteRequestAtaCommand(ChannelExtension, Srb, Cdb); // // Set LBA bits 0 - 23 first. // cfis->LBA7_0 = (UCHAR)(((startingSector.LowPart) & 0x000000ff) >> 0); cfis->LBA15_8 = (UCHAR)(((startingSector.LowPart) & 0x0000ff00) >> 8); cfis->LBA23_16 = (UCHAR)(((startingSector.LowPart) & 0x00ff0000) >> 16); if (IsNCQCommand(srbExtension) || Support48Bit(&ChannelExtension->DeviceExtension->DeviceParameters)) { // // Set LBA bits 24 - 47 for devices that support 48bits commands. // cfis->LBA31_24 = (UCHAR)(((startingSector.LowPart) & 0xff000000) >> 24); cfis->LBA39_32 = (UCHAR)(((startingSector.HighPart) & 0x000000ff) >> 0); cfis->LBA47_40 = (UCHAR)(((startingSector.HighPart) & 0x0000ff00) >> 8); } else { // // Set LBA bits 24 - 27 for devices that support 28bits commands only. // cfis->Device = (UCHAR)(((startingSector.LowPart) & 0x0f000000) >> 24); } // // Set Sector Counts and Device fields. // if (IsNCQCommand(srbExtension)) { cfis->Feature7_0 = (UCHAR)((sectorCount & 0x000000ff) >> 0); cfis->Feature15_8 = (UCHAR)((sectorCount & 0x0000ff00) >> 8); cfis->Device |= IDE_LBA_MODE; if( (Cdb->CDB10.ForceUnitAccess == 1) && IsFuaSupported(ChannelExtension) ){ cfis->Device |= ATA_NCQ_FUA_BIT; } else { cfis->Device &= ~ATA_NCQ_FUA_BIT; } } else { cfis->Count7_0 = (UCHAR)((sectorCount & 0x000000ff) >> 0); if (Support48Bit(&ChannelExtension->DeviceExtension->DeviceParameters)) { cfis->Count15_8 = (UCHAR)((sectorCount & 0x0000ff00) >> 8); } else { // we cannot handle more than 256 sectors NT_ASSERT(sectorCount <= 0x100); // The starting LBA should be less than 28 bits wide NT_ASSERT(startingSector.HighPart == 0); // The upper layers could still send us sectors beyond the // end of the disk. Do not assert. Let the device fail it. // //NT_ASSERT((startingSector.LowPart & 0xF0000000) == 0); } cfis->Device |= (0xA0 | IDE_LBA_MODE); } // // Normal Stack, cache priority info can be passed with READ or WRITE request. // For Hiberfile, only pass cache priority info for WRITE request. // if ((!IsDumpMode(ChannelExtension->AdapterExtension) && IsDeviceHybridInfoEnabled(ChannelExtension) && IsNcqReadWriteCommand(srbExtension)) || ((ChannelExtension->StateFlags.HybridInfoEnabledOnHiberFile == 1) && IsNCQWriteCommand(srbExtension))) { PSRBEX_DATA_IO_INFO srbExInfo = (PSRBEX_DATA_IO_INFO)SrbGetSrbExDataByType(Srb, SrbExDataTypeIoInfo); if ((srbExInfo != NULL) && ((srbExInfo->Flags & REQUEST_INFO_VALID_CACHEPRIORITY_FLAG) != 0) && (srbExInfo->CachePriority <= ChannelExtension->DeviceExtension->HybridInfo.MaximumHybridPriorityLevel)) { ATA_HYBRID_INFO_FIELDS hybridInfo = {0}; hybridInfo.InfoValid = 1; hybridInfo.HybridPriority = srbExInfo->CachePriority; cfis->Auxiliary23_16 = hybridInfo.AsUchar; // // For WRITE request that requires HybridPassThrough, assign completion routine // to issue EVICT command for evicting data out of caching medium. // if (!IsDumpMode(ChannelExtension->AdapterExtension) && IsNCQWriteCommand(srbExtension) && ((srbExInfo->Flags & REQUEST_INFO_HYBRID_WRITE_THROUGH_FLAG) != 0) && (ChannelExtension->DeviceExtension->SupportedCommands.HybridEvict == 1)) { srbExtension->CompletionRoutine = HybridWriteThroughCompletion; } hybridPriorityPassedIn = TRUE; } } return; } ULONG AtaReadWriteRequest ( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ PSTORAGE_REQUEST_BLOCK Srb, _In_ PCDB Cdb, _In_ ULONG CdbLength ) { PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(Srb); ULONG srbFlags = SrbGetSrbFlags(Srb); if (!IsAtaDevice(&ChannelExtension->DeviceExtension->DeviceParameters) || (ChannelExtension->DeviceExtension->DeviceParameters.BytesPerLogicalSector == 0) ) { Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; return STOR_STATUS_INVALID_PARAMETER; } // // A read write command must have this flag set. // The absence of the flag indicates a null MDL. We // could get such an SRB with a bad passthrough // request. Protect against it. (This might look like // a hack, but it is not. We should verify our assumptions // before we do a translation) // if (!(srbFlags & SRB_FLAGS_UNSPECIFIED_DIRECTION)) { Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; return STOR_STATUS_INVALID_PARAMETER; } // set the transfer mode to be used srbExtension->Flags |= ATA_FLAGS_USE_DMA; if (Support48Bit(&ChannelExtension->DeviceExtension->DeviceParameters)) { srbExtension->Flags |= ATA_FLAGS_48BIT_COMMAND; } if (IsSupportedReadCdb(Cdb)) { srbExtension->Flags |= ATA_FLAGS_DATA_IN; } else if (IsSupportedWriteCdb(Cdb)) { srbExtension->Flags |= ATA_FLAGS_DATA_OUT; } else { // // other READ/WRITE commands are not supported by StorAHCI. // Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; return STOR_STATUS_INVALID_PARAMETER; } BuildReadWriteCommand(ChannelExtension, Srb, Cdb, CdbLength); return STOR_STATUS_SUCCESS; } ULONG AtaVerifyRequest( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ PSTORAGE_REQUEST_BLOCK Srb, _In_ PCDB Cdb, _In_ ULONG CdbLength ) /*++ Routine Description: This routine handles IDE Verify. Arguments: HwDeviceExtension - HBA miniport driver's adapter data storage Srb - IO request packet Cdb - SCSI command carried by Srb CdbLength - length of the SCSI command Return Value: SRB status --*/ { LARGE_INTEGER startingSector; ULONG sectorCount; UCHAR commandReg; PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(Srb); // Get starting sector number from CDB. startingSector.QuadPart = GetLbaFromCdb(Cdb, CdbLength); // get the sector count from the cdb sectorCount = GetSectorCountFromCdb(Cdb, CdbLength); // Ensure that the command is small enough for us to handle if (sectorCount > 0x10000) { // Sector count cannot be longer than 2 bytes (2 ^ 16). // Note that a 0 sector count => 0x10000 sectors Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; return STOR_STATUS_INVALID_PARAMETER; } if (!Support48Bit(&ChannelExtension->DeviceExtension->DeviceParameters) && (sectorCount > 0x100)) { // Without 48bit support the sector count cannot be greater than 256 sectors Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; return STOR_STATUS_INVALID_PARAMETER; } srbExtension->AtaFunction = ATA_FUNCTION_ATA_COMMAND; if (Support48Bit(&ChannelExtension->DeviceExtension->DeviceParameters)) { srbExtension->Flags |= ATA_FLAGS_48BIT_COMMAND; } AtaSetTaskFileDataRange(ChannelExtension, Srb, startingSector.QuadPart, sectorCount ); // verify command. no data transfer if (Support48Bit(&ChannelExtension->DeviceExtension->DeviceParameters)) { commandReg = IDE_COMMAND_VERIFY_EXT; } else { commandReg = IDE_COMMAND_VERIFY; } SetCommandReg((&srbExtension->TaskFile.Current), commandReg); return STOR_STATUS_SUCCESS; } VOID AtaModeSenseRequestCompletionMediaStatus ( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ PSTORAGE_REQUEST_BLOCK Srb ) { PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(Srb); PMODE_PARAMETER_HEADER modePageHeader = (PMODE_PARAMETER_HEADER)SrbGetDataBuffer(Srb); ULONG transferLength = SrbGetDataTransferLength(Srb); UNREFERENCED_PARAMETER(ChannelExtension); if (Srb->SrbStatus == SRB_STATUS_BUS_RESET) { return; } if ( transferLength < sizeof(PMODE_PARAMETER_HEADER) ) { NT_ASSERT(FALSE); return; } // update the mode page header NT_ASSERT(modePageHeader != NULL); if ( (srbExtension->AtaStatus & IDE_STATUS_ERROR) && (srbExtension->AtaError & IDE_ERROR_DATA_ERROR) ) { modePageHeader->DeviceSpecificParameter |= MODE_DSP_WRITE_PROTECT; } return; } VOID AtaModeSenseRequestCompletionWriteCache ( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ PSTORAGE_REQUEST_BLOCK Srb ) { PMODE_CACHING_PAGE cachePage; ULONG cdbLength = 0; PCDB cdb = RequestGetSrbScsiData(Srb, &cdbLength, NULL, NULL, NULL); PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(Srb); PVOID modePageHeader = SrbGetDataBuffer(Srb); PIDENTIFY_DEVICE_DATA identifyDeviceData = (PIDENTIFY_DEVICE_DATA)srbExtension->DataBuffer; ULONG srbDataBufferLength = SrbGetDataTransferLength(Srb); ULONG modePageHeaderSize; //Initialize variables NT_ASSERT(identifyDeviceData != NULL); if (cdb == NULL) { NT_ASSERT(cdb != NULL); return; } if (cdb->MODE_SENSE.OperationCode == SCSIOP_MODE_SENSE) { modePageHeaderSize = sizeof(MODE_PARAMETER_HEADER); } else { NT_ASSERT(cdb->MODE_SENSE.OperationCode == SCSIOP_MODE_SENSE10); modePageHeaderSize = sizeof(MODE_PARAMETER_HEADER10); } cachePage = (PMODE_CACHING_PAGE)(((PCHAR)modePageHeader) + modePageHeaderSize); if (Srb->SrbStatus == SRB_STATUS_BUS_RESET) { return; } // update the mode page header if ( (modePageHeader == NULL) || (srbDataBufferLength < ( modePageHeaderSize + sizeof(PMODE_CACHING_PAGE) ) ) ) { NT_ASSERT(modePageHeader != NULL); Srb->SrbStatus = SRB_STATUS_ERROR; return; } if (Srb->SrbStatus == SRB_STATUS_SUCCESS) { cachePage->WriteCacheEnable = (UCHAR)identifyDeviceData->CommandSetActive.WriteCache; } else { Srb->SrbStatus = SRB_STATUS_ERROR; } // update the number of bytes we are returning. If this function executes, the maximum size is being used. SrbSetDataTransferLength(Srb, srbDataBufferLength - modePageHeaderSize - sizeof(MODE_CACHING_PAGE)); if (IsRemovableMedia(&ChannelExtension->DeviceExtension->DeviceParameters) && IsMsnEnabled(ChannelExtension->DeviceExtension)) { // prepare to send no data command. reuse srbExtension area, clear it first AhciZeroMemory((PCHAR)srbExtension, sizeof(AHCI_SRB_EXTENSION)); srbExtension->AtaFunction = ATA_FUNCTION_ATA_COMMAND; SetCommandReg((&srbExtension->TaskFile.Current), IDE_COMMAND_GET_MEDIA_STATUS); return; } return; } ULONG AtaInitModePageHeaderWrapper ( _In_ PCDB Cdb, _In_reads_bytes_(DataBufferLength) PVOID ModePageHeader, _In_ ULONG DataBufferLength, _Out_ PMODE_PARAMETER_HEADER_WRAPPER ModePageHeaderWrapper ) /*++ Routine Description: This routine initializes a MODE_PARAMETER_HEADER_WRAPPER taking into account the command in Cdb (SCSI_OP_MODE_SENSE or SCSI_OP_MODE_SENSE10). Arguments: Cdb - Cdb of a MODE SENSE SCSI command. ModePageHeader - A pointer to either a MODE_PARAMETER_HEADER or MODE_PARAMETER_HEADER_10 structure. ULONG DataBufferLength - The size of the buffer that contains ModePageHeader. ModePageHeaderWrapper - Pointer to a MODE_PARAMETER_HEADER_WRAPPER that will be filled by this routine. Return Value: STOR_STATUS_SUCCESS if the routine is successful, or STOR_STATUS_INSUFFICIENT_RESOURCES if the ModePageHeader buffer isn't big enough. --*/ { NT_ASSERT(ModePageHeader != NULL); AhciZeroMemory((PCHAR)ModePageHeader, DataBufferLength); ModePageHeaderWrapper->ModePageHeader = ModePageHeader; if (Cdb->MODE_SENSE.OperationCode == SCSIOP_MODE_SENSE) { PMODE_PARAMETER_HEADER modePageHeader6 = (PMODE_PARAMETER_HEADER)ModePageHeader; ModePageHeaderWrapper->HeaderSize = sizeof(*modePageHeader6); if (DataBufferLength < ModePageHeaderWrapper->HeaderSize) { return STOR_STATUS_INVALID_PARAMETER; } ModePageHeaderWrapper->DeviceSpecificParameter = &modePageHeader6->DeviceSpecificParameter; ModePageHeaderWrapper->ModeDataLength = &modePageHeader6->ModeDataLength; *(ModePageHeaderWrapper->ModeDataLength) = sizeof(MODE_PARAMETER_HEADER) - FIELD_OFFSET(MODE_PARAMETER_HEADER, MediumType); } else { USHORT modeDataLength; PMODE_PARAMETER_HEADER10 modePageHeader10 = (PMODE_PARAMETER_HEADER10)ModePageHeader; NT_ASSERT(Cdb->MODE_SENSE.OperationCode == SCSIOP_MODE_SENSE10); ModePageHeaderWrapper->HeaderSize = sizeof(*modePageHeader10); if (DataBufferLength < ModePageHeaderWrapper->HeaderSize) { return STOR_STATUS_INVALID_PARAMETER; } ModePageHeaderWrapper->DeviceSpecificParameter = &modePageHeader10->DeviceSpecificParameter; // // The ModeDataLength field of a MODE SENSE 10 command is 2 bytes long, but we only // expose one byte in the wrapper structure. This restriction allows code that uses // the wrapper to work independently of the underlying header structure, since a MODE SENSE 6 // command only has 1 byte for ModeDataLength. // // The 2nd byte of ModeDataLength is the least significant. ModePageHeaderWrapper->ModeDataLength = &modePageHeader10->ModeDataLength[1]; modeDataLength = sizeof(MODE_PARAMETER_HEADER10) - FIELD_OFFSET(MODE_PARAMETER_HEADER10, MediumType); NT_ASSERT(modeDataLength <= 0xFF); *(ModePageHeaderWrapper->ModeDataLength) = (UCHAR)modeDataLength; } return STOR_STATUS_SUCCESS; } ULONG AtaModeSenseRequest ( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ PSTORAGE_REQUEST_BLOCK Srb, _In_ PCDB Cdb ) /* NOTE: only Cache Page is supported by storahci driver */ { ULONG bytesLeft; ULONG status; PVOID modePageHeader = NULL; MODE_PARAMETER_HEADER_WRAPPER modePageHeaderWrapper = { 0 }; PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(Srb); ULONG srbDataBufferLength = SrbGetDataTransferLength(Srb); // only support page control for current values if (Cdb->MODE_SENSE.Pc != 0) { Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; return STOR_STATUS_INVALID_PARAMETER; } // modePageHeader can be PMODE_PARAMETER_HEADER or PMODE_PARAMETER_HEADER10, // depending on the command opcode. This is abstracted away by using // a wrapper. modePageHeader = SrbGetDataBuffer(Srb); if (modePageHeader == NULL) { Srb->SrbStatus = SRB_STATUS_INTERNAL_ERROR; //Srb->InternalStatus = STATUS_INSUFFICIENT_RESOURCES; return STOR_STATUS_INSUFFICIENT_RESOURCES; } // initialize to success Srb->SrbStatus = SRB_STATUS_SUCCESS; // this initializes the wrapper structure and sets the ModeDataLength // field status = AtaInitModePageHeaderWrapper(Cdb, modePageHeader, srbDataBufferLength, &modePageHeaderWrapper); if (status != STOR_STATUS_SUCCESS) { Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; return status; } // now service the cache page bytesLeft = srbDataBufferLength - modePageHeaderWrapper.HeaderSize; if ((Cdb->MODE_SENSE.PageCode == MODE_SENSE_RETURN_ALL) || (Cdb->MODE_SENSE.PageCode == MODE_PAGE_CACHING)) { if (bytesLeft >= sizeof(MODE_CACHING_PAGE)) { // cache settings page PMODE_CACHING_PAGE cachePage; cachePage = (PMODE_CACHING_PAGE)(((PCHAR) modePageHeaderWrapper.ModePageHeader) + modePageHeaderWrapper.HeaderSize); cachePage->PageCode = MODE_PAGE_CACHING; cachePage->PageSavable = 0; cachePage->PageLength = 0xa; cachePage->ReadDisableCache = 0; cachePage->WriteCacheEnable = 0; //initialized value, likely to change below if (IsFuaSupported(ChannelExtension)) { *(modePageHeaderWrapper.DeviceSpecificParameter) |= MODE_DSP_FUA_SUPPORTED; } bytesLeft -= sizeof(MODE_CACHING_PAGE); *(modePageHeaderWrapper.ModeDataLength) += sizeof(MODE_CACHING_PAGE); //Check to see if the Write Cache is enabled on this device, by issuing an IDENTIFY DEVICE command if (ChannelExtension->DeviceExtension->IdentifyDeviceData->CommandSetSupport.WriteCache != 0) { srbExtension->AtaFunction = ATA_FUNCTION_ATA_IDENTIFY; srbExtension->Flags |= ATA_FLAGS_DATA_IN; srbExtension->DataBuffer = ChannelExtension->DeviceExtension->IdentifyDeviceData; srbExtension->DataTransferLength = sizeof(IDENTIFY_DEVICE_DATA); srbExtension->LocalSgl.NumberOfElements = 1; srbExtension->LocalSgl.List[0].PhysicalAddress.LowPart = ChannelExtension->DeviceExtension->IdentifyDataPhysicalAddress.LowPart; srbExtension->LocalSgl.List[0].PhysicalAddress.HighPart = ChannelExtension->DeviceExtension->IdentifyDataPhysicalAddress.HighPart; srbExtension->LocalSgl.List[0].Length = sizeof(IDENTIFY_DEVICE_DATA); srbExtension->Sgl = &srbExtension->LocalSgl; srbExtension->CompletionRoutine = AtaModeSenseRequestCompletionWriteCache; SetCommandReg((&srbExtension->TaskFile.Current), IDE_COMMAND_IDENTIFY); return STOR_STATUS_SUCCESS; //Get Media Protect Status will be preformed in AtaModeSenseRequestCompletionWriteCache. } } else { Srb->SrbStatus = SRB_STATUS_DATA_OVERRUN; } } // update the number of bytes we are returning SrbSetDataTransferLength(Srb, srbDataBufferLength - bytesLeft); if (IsRemovableMedia(&ChannelExtension->DeviceExtension->DeviceParameters) && IsMsnEnabled(ChannelExtension->DeviceExtension)) { // this command does not transfer data srbExtension->AtaFunction = ATA_FUNCTION_ATA_COMMAND; srbExtension->CompletionRoutine = AtaModeSenseRequestCompletionMediaStatus; SetCommandReg((&srbExtension->TaskFile.Current), IDE_COMMAND_GET_MEDIA_STATUS); } return STOR_STATUS_SUCCESS; } ULONG AtaModeSelectRequest ( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ PSTORAGE_REQUEST_BLOCK Srb, _In_ PCDB Cdb ) /* NOTE: this is used to enable/disable device write cache. */ { ULONG status = STOR_STATUS_SUCCESS; PMODE_PARAMETER_HEADER modePageHeader; PMODE_CACHING_PAGE cachePage; ULONG pageOffset; ULONG bytesLeft; PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(Srb); ULONG srbDataBufferLength = SrbGetDataTransferLength(Srb); NT_ASSERT( (Cdb->MODE_SELECT.OperationCode == SCSIOP_MODE_SELECT) || (Cdb->MODE_SELECT.OperationCode == SCSIOP_MODE_SELECT10) ); // only support scsi-2 mode select format if (Cdb->MODE_SELECT.PFBit != 1) { Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; return STOR_STATUS_INVALID_DEVICE_REQUEST; } if (srbDataBufferLength < sizeof(MODE_PARAMETER_HEADER)) { Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; return STOR_STATUS_INVALID_DEVICE_REQUEST; } modePageHeader = (PMODE_PARAMETER_HEADER)SrbGetDataBuffer(Srb); if (modePageHeader == NULL) { Srb->SrbStatus = SRB_STATUS_INTERNAL_ERROR; //Srb->InternalStatus = STATUS_INSUFFICIENT_RESOURCES; return STOR_STATUS_INSUFFICIENT_RESOURCES; } pageOffset = sizeof(MODE_PARAMETER_HEADER) + modePageHeader->BlockDescriptorLength; if (srbDataBufferLength > pageOffset) { bytesLeft = srbDataBufferLength - pageOffset; } else { bytesLeft = 0; } cachePage = (PMODE_CACHING_PAGE)(((PUCHAR) modePageHeader) + pageOffset); if ( (bytesLeft >= sizeof(MODE_CACHING_PAGE)) && (cachePage->PageCode == MODE_PAGE_CACHING) && (cachePage->PageLength == 0xa) ) { //if Write Cache is supported if (ChannelExtension->DeviceExtension->IdentifyDeviceData->CommandSetSupport.WriteCache != 0) { srbExtension->AtaFunction = ATA_FUNCTION_ATA_COMMAND; if (cachePage->WriteCacheEnable != 0) { SetFeaturesReg ((&srbExtension->TaskFile.Current), IDE_FEATURE_ENABLE_WRITE_CACHE); } else { SetFeaturesReg ((&srbExtension->TaskFile.Current), IDE_FEATURE_DISABLE_WRITE_CACHE); } SetCommandReg ((&srbExtension->TaskFile.Current), IDE_COMMAND_SET_FEATURE); } else { // we are in the same cache state Srb->SrbStatus = SRB_STATUS_SUCCESS; status = STOR_STATUS_SUCCESS; } } else { // the request is not for the mode cache page Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; status = STOR_STATUS_INVALID_PARAMETER; } return status; } VOID SelectDeviceGeometry( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ PATA_DEVICE_PARAMETERS DeviceParameters, _In_ PIDENTIFY_DEVICE_DATA IdentifyDeviceData ) { LARGE_INTEGER maxLba; UNREFERENCED_PARAMETER(ChannelExtension); // ignore non ata devices if (!IsAtaDevice(DeviceParameters)) { return; } maxLba.QuadPart = 0; if (Support48Bit(DeviceParameters)) { maxLba.LowPart = IdentifyDeviceData->Max48BitLBA[0]; maxLba.HighPart = IdentifyDeviceData->Max48BitLBA[1]; } else { maxLba.LowPart = IdentifyDeviceData->UserAddressableSectors; } DeviceParameters->MaxLba = maxLba; // assume 512 bytes/sector if( IdentifyDeviceData->PhysicalLogicalSectorSize.LogicalSectorLongerThan256Words ) { DeviceParameters->BytesPerLogicalSector = IdentifyDeviceData->WordsPerLogicalSector[0] + (IdentifyDeviceData->WordsPerLogicalSector[1] << 16); DeviceParameters->BytesPerLogicalSector <<= 1; } if (DeviceParameters->BytesPerLogicalSector < ATA_BLOCK_SIZE) { DeviceParameters->BytesPerLogicalSector = ATA_BLOCK_SIZE; } if( IdentifyDeviceData->PhysicalLogicalSectorSize.MultipleLogicalSectorsPerPhysicalSector ){ DeviceParameters->BytesPerPhysicalSector = (1 << IdentifyDeviceData->PhysicalLogicalSectorSize.LogicalSectorsPerPhysicalSector) * DeviceParameters->BytesPerLogicalSector; } if (DeviceParameters->BytesPerPhysicalSector < DeviceParameters->BytesPerLogicalSector) { DeviceParameters->BytesPerPhysicalSector = DeviceParameters->BytesPerLogicalSector; } if( IdentifyDeviceData->BlockAlignment.Word209Supported) { DeviceParameters->BytesOffsetForSectorAlignment = IdentifyDeviceData->BlockAlignment.AlignmentOfLogicalWithinPhysical * DeviceParameters->BytesPerLogicalSector; } return; } ULONG AtaReadCapacityCompletion ( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ PSTORAGE_REQUEST_BLOCK Srb ) { ULONG status; ULONG bytesPerSector; ULONG64 maxLba; PVOID srbDataBuffer = SrbGetDataBuffer(Srb); ULONG srbDataBufferLength = SrbGetDataTransferLength(Srb); UCHAR cdbLength = SrbGetCdbLength(Srb); NT_ASSERT(!IsAtapiDevice(&ChannelExtension->DeviceExtension->DeviceParameters)); maxLba = MaxUserAddressableLba(&ChannelExtension->DeviceExtension->DeviceParameters) - 1; bytesPerSector = BytesPerLogicalSector(&ChannelExtension->DeviceExtension->DeviceParameters); // fill in read capacity if (srbDataBuffer != NULL) { if (cdbLength == 0x10) { // 16 byte CDB PREAD_CAPACITY16_DATA readCap16 = (PREAD_CAPACITY16_DATA)srbDataBuffer; ULONG returnDataLength = 12; //default to sizeof(READ_CAPACITY_DATA_EX) if (srbDataBufferLength < sizeof(READ_CAPACITY_DATA_EX)) { Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; return STOR_STATUS_BUFFER_TOO_SMALL; } NT_ASSERT(SrbGetCdb(Srb)->CDB10.OperationCode == SCSIOP_READ_CAPACITY16); REVERSE_BYTES_QUAD(&readCap16->LogicalBlockAddress.QuadPart, &maxLba); REVERSE_BYTES(&readCap16->BytesPerBlock, &bytesPerSector); if (srbDataBufferLength >= FIELD_OFFSET(READ_CAPACITY16_DATA, Reserved3)) { // buffer is big enough for sector alignment info readCap16->ProtectionEnable = 0; // not support Portection Types. readCap16->ProtectionType = 0; readCap16->LogicalPerPhysicalExponent = (UCHAR)ChannelExtension->DeviceExtension->IdentifyDeviceData->PhysicalLogicalSectorSize.LogicalSectorsPerPhysicalSector; if(ChannelExtension->DeviceExtension->IdentifyDeviceData->BlockAlignment.Word209Supported) { USHORT logicalBlocksPerPhysicalBlock = (USHORT)(1 << readCap16->LogicalPerPhysicalExponent); USHORT lowestAlignedBlock = (logicalBlocksPerPhysicalBlock - ChannelExtension->DeviceExtension->IdentifyDeviceData->BlockAlignment.AlignmentOfLogicalWithinPhysical) % logicalBlocksPerPhysicalBlock; readCap16->LowestAlignedBlock_MSB = (UCHAR)((lowestAlignedBlock >> 8) & 0x00FF); readCap16->LowestAlignedBlock_LSB = (UCHAR)(lowestAlignedBlock & 0x00FF); } else { readCap16->LowestAlignedBlock_LSB = 0; readCap16->LowestAlignedBlock_MSB = 0; } returnDataLength = FIELD_OFFSET(READ_CAPACITY16_DATA, Reserved3); } SrbSetDataTransferLength(Srb, returnDataLength); } else { // 12 byte CDB PREAD_CAPACITY_DATA readCap = (PREAD_CAPACITY_DATA)srbDataBuffer; NT_ASSERT(SrbGetCdb(Srb)->CDB10.OperationCode == SCSIOP_READ_CAPACITY); if (srbDataBufferLength < sizeof(READ_CAPACITY_DATA)) { Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; return STOR_STATUS_BUFFER_TOO_SMALL; } REVERSE_BYTES(&readCap->BytesPerBlock, &bytesPerSector); if (maxLba >= MAXULONG) { readCap->LogicalBlockAddress = MAXULONG; } else { ULONG tmp = (ULONG)(maxLba & MAXULONG); REVERSE_BYTES(&readCap->LogicalBlockAddress, &tmp); } SrbSetDataTransferLength(Srb, sizeof(READ_CAPACITY_DATA)); } Srb->SrbStatus = SRB_STATUS_SUCCESS; status = STOR_STATUS_SUCCESS; } else { Srb->SrbStatus = SRB_STATUS_INTERNAL_ERROR; status = STOR_STATUS_INSUFFICIENT_RESOURCES; } return status; } VOID AtaReadCapacityRequestCompletion ( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ PSTORAGE_REQUEST_BLOCK Srb ) { ULONG status = STOR_STATUS_SUCCESS; PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(Srb); PUCHAR identifyDeviceData = (PUCHAR)srbExtension->DataBuffer; NT_ASSERT(IsRemovableMedia(&ChannelExtension->DeviceExtension->DeviceParameters)); if (Srb->SrbStatus == SRB_STATUS_SUCCESS) { ATA_DEVICE_PARAMETERS deviceParameters; // make a local copy StorPortCopyMemory(&deviceParameters, &ChannelExtension->DeviceExtension->DeviceParameters, sizeof(ATA_DEVICE_PARAMETERS)); // modify the fields related to media geometry SelectDeviceGeometry(ChannelExtension, &deviceParameters, (PIDENTIFY_DEVICE_DATA)identifyDeviceData); if (deviceParameters.MaxLba.QuadPart > 1) { // the device returned some geometry // update the one in the device extension StorPortCopyMemory(&ChannelExtension->DeviceExtension->DeviceParameters, &deviceParameters, sizeof(ATA_DEVICE_PARAMETERS)); status = AtaReadCapacityCompletion(ChannelExtension, Srb); } else { SENSE_DATA senseBuffer; PVOID srbSenseBuffer = NULL; UCHAR srbSenseBufferLength = 0; RequestGetSrbScsiData(Srb, NULL, NULL, &srbSenseBuffer, &srbSenseBufferLength); // no media in device senseBuffer.ErrorCode = 0x70; senseBuffer.Valid = 1; senseBuffer.AdditionalSenseLength = 0xb; senseBuffer.SenseKey = SCSI_SENSE_NOT_READY; senseBuffer.AdditionalSenseCode = SCSI_ADSENSE_NO_MEDIA_IN_DEVICE; senseBuffer.AdditionalSenseCodeQualifier = 0; SrbSetDataTransferLength(Srb, 0); Srb->SrbStatus = SRB_STATUS_ERROR; //Srb->ScsiStatus = SCSISTAT_CHECK_CONDITION; // update the sense buffer if (srbSenseBuffer != NULL) { UCHAR senseLen = min(srbSenseBufferLength, sizeof(SENSE_DATA)); if (senseLen > 0) { StorPortCopyMemory(srbSenseBuffer, &senseBuffer, senseLen); Srb->SrbStatus |= SRB_STATUS_AUTOSENSE_VALID; } } status = STOR_STATUS_UNSUCCESSFUL; } } else { SrbSetDataTransferLength(Srb, 0); if (Srb->SrbStatus != SRB_STATUS_BUS_RESET) { Srb->SrbStatus = SRB_STATUS_SELECTION_TIMEOUT; } status = STOR_STATUS_UNSUCCESSFUL; } UNREFERENCED_PARAMETER(status); return; } ULONG AtaReadCapacityRequest ( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ PSTORAGE_REQUEST_BLOCK Srb, _In_ PCDB Cdb, _In_ ULONG CdbLength ) { ULONG status = STOR_STATUS_SUCCESS; PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(Srb); ULONG srbDataBufferLength = SrbGetDataTransferLength(Srb); NT_ASSERT(!IsAtapiDevice(&ChannelExtension->DeviceExtension->DeviceParameters)); // Verify the data transfer length if (srbDataBufferLength < sizeof(READ_CAPACITY_DATA)) { Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; return STOR_STATUS_BUFFER_TOO_SMALL; } if ((CdbLength == 0x10) && (srbDataBufferLength < sizeof(READ_CAPACITY_DATA_EX))) { // classpnp may send down this request with buffer size = sizeof(READ_CAPACITY_DATA_EX) NT_ASSERT(SrbGetCdb(Srb)->CDB10.OperationCode == SCSIOP_READ_CAPACITY16); Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; return STOR_STATUS_BUFFER_TOO_SMALL; } if ((CdbLength == 0x10) && (Cdb->CDB16.OperationCode == SCSIOP_READ_CAPACITY16) && (Cdb->READ_CAPACITY16.ServiceAction != SERVICE_ACTION_READ_CAPACITY16)) { Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; return STOR_STATUS_INVALID_DEVICE_REQUEST; } if (IsRemovableMedia(&ChannelExtension->DeviceExtension->DeviceParameters)) { // get identify data from the device as the media might be changed. srbExtension->AtaFunction = ATA_FUNCTION_ATA_IDENTIFY; srbExtension->Flags |= ATA_FLAGS_DATA_IN; srbExtension->DataBuffer = ChannelExtension->DeviceExtension->IdentifyDeviceData; srbExtension->DataTransferLength = sizeof(IDENTIFY_DEVICE_DATA); srbExtension->CompletionRoutine = AtaReadCapacityRequestCompletion; srbExtension->LocalSgl.NumberOfElements = 1; srbExtension->LocalSgl.List[0].PhysicalAddress.LowPart = ChannelExtension->DeviceExtension->IdentifyDataPhysicalAddress.LowPart; srbExtension->LocalSgl.List[0].PhysicalAddress.HighPart = ChannelExtension->DeviceExtension->IdentifyDataPhysicalAddress.HighPart; srbExtension->LocalSgl.List[0].Length = sizeof(IDENTIFY_DEVICE_DATA); srbExtension->Sgl = &srbExtension->LocalSgl; SetCommandReg((&srbExtension->TaskFile.Current), IDE_COMMAND_IDENTIFY); } else { // fixed media status = AtaReadCapacityCompletion(ChannelExtension, Srb); } return status; } VOID AtaGenerateInquiryData ( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_reads_bytes_(ATA_INQUIRYDATA_SIZE) PINQUIRYDATA InquiryData ) { USHORT descriptor = VER_DESCRIPTOR_1667_NOVERSION; ULONG len; ULONG vendorIdLength; ULONG prodLen; AhciZeroMemory((PCHAR)InquiryData, ATA_INQUIRYDATA_SIZE); InquiryData->DeviceType = ChannelExtension->DeviceExtension->DeviceParameters.ScsiDeviceType; //DIRECT_ACCESS_DEVICE; InquiryData->RemovableMedia = IsRemovableMedia(&ChannelExtension->DeviceExtension->DeviceParameters) ? 1 : 0; InquiryData->ResponseDataFormat = 0x2; //data format is defined in SPC standard InquiryData->CommandQueue = 1; //support NCQ for AHCI controller InquiryData->AdditionalLength = ATA_INQUIRYDATA_SIZE - RTL_SIZEOF_THROUGH_FIELD(INQUIRYDATA, AdditionalLength); // if there is blank space in first 8 chars, use the part before blank space as VendorId for (vendorIdLength = 8; vendorIdLength > 0; vendorIdLength--) { if (ChannelExtension->DeviceExtension->DeviceParameters.VendorId[vendorIdLength - 1] == ' ') { break; } } len = min(vendorIdLength, sizeof(ChannelExtension->DeviceExtension->DeviceParameters.VendorId)); AhciFillMemory((PCHAR)InquiryData->VendorId, 8, ' '); // if there is no blank space in first 8 chars, leave blank spaces in VendorId. Otherwise, copy the string if (len > 0 && len < 9) { StorPortCopyMemory(InquiryData->VendorId, ChannelExtension->DeviceExtension->DeviceParameters.VendorId, len ); } prodLen = min(16, sizeof(ChannelExtension->DeviceExtension->DeviceParameters.VendorId) - len); AhciFillMemory((PCHAR)InquiryData->ProductId, 16, ' '); StorPortCopyMemory(InquiryData->ProductId, (ChannelExtension->DeviceExtension->DeviceParameters.VendorId + len), prodLen ); len = min(4, sizeof(ChannelExtension->DeviceExtension->DeviceParameters.RevisionId)); AhciFillMemory((PCHAR)InquiryData->ProductRevisionLevel, 4, ' '); StorPortCopyMemory(InquiryData->ProductRevisionLevel, ChannelExtension->DeviceExtension->DeviceParameters.RevisionId, len ); if ( (ChannelExtension->DeviceExtension->IdentifyDeviceData->TrustedComputing.FeatureSupported == 1) && (ChannelExtension->DeviceExtension->IdentifyDeviceData->AdditionalSupported.IEEE1667 == 1) ) { // fill in 1667 descriptor REVERSE_BYTES_SHORT(&InquiryData->VersionDescriptors[0], &descriptor); } return; } VOID IssueIdentifyCommand( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ PSTORAGE_REQUEST_BLOCK Srb ) /*++ This could be a macro. broken out here to make the logic easier to read It assumes: nothing Called by: AtaInquiryRequest AtaReportLunsCommand It performs: 1 Fills in the local SRB with the SetFeatures command 2 Starts processing the command Affected Variables/Registers: none --*/ { PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(Srb); //1 Fills in the local SRB srbExtension->AtaFunction = ATA_FUNCTION_ATA_IDENTIFY; srbExtension->Flags |= ATA_FLAGS_DATA_IN; srbExtension->CompletionRoutine = AhciPortIdentifyDevice; //setup TaskFile srbExtension->TaskFile.Current.bFeaturesReg = 0; srbExtension->TaskFile.Current.bSectorCountReg = 0; srbExtension->TaskFile.Current.bSectorNumberReg = 0; srbExtension->TaskFile.Current.bCylLowReg = 0; srbExtension->TaskFile.Current.bCylHighReg = 0; srbExtension->TaskFile.Current.bDriveHeadReg = 0xA0; srbExtension->TaskFile.Current.bCommandReg = IDE_COMMAND_NOT_VALID; //the command will be set when we can read PxSIG register. srbExtension->TaskFile.Current.bReserved = 0; srbExtension->TaskFile.Previous.bFeaturesReg = 0; srbExtension->TaskFile.Previous.bSectorCountReg = 0; srbExtension->TaskFile.Previous.bSectorNumberReg = 0; srbExtension->TaskFile.Previous.bCylLowReg = 0; srbExtension->TaskFile.Previous.bCylHighReg = 0; srbExtension->TaskFile.Previous.bDriveHeadReg = 0; srbExtension->TaskFile.Previous.bCommandReg = 0; srbExtension->TaskFile.Previous.bReserved = 0; Srb->SrbStatus = SRB_STATUS_PENDING; srbExtension->DataBuffer = (PVOID)ChannelExtension->DeviceExtension->IdentifyDeviceData; srbExtension->LocalSgl.NumberOfElements = 1; srbExtension->LocalSgl.List[0].PhysicalAddress.LowPart = ChannelExtension->DeviceExtension->IdentifyDataPhysicalAddress.LowPart; srbExtension->LocalSgl.List[0].PhysicalAddress.HighPart = ChannelExtension->DeviceExtension->IdentifyDataPhysicalAddress.HighPart; srbExtension->LocalSgl.List[0].Length = sizeof(IDENTIFY_DEVICE_DATA); srbExtension->Sgl = &srbExtension->LocalSgl; srbExtension->DataTransferLength = sizeof(IDENTIFY_DEVICE_DATA); return; } ULONG InquiryComplete( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ PSTORAGE_REQUEST_BLOCK Srb ) /*++ This is the completion point of INQUIRY command for ATA devices. Arguments: ChannelExtension Srb Return Value: ULONG - status --*/ { ULONG status = STOR_STATUS_SUCCESS; ULONG dataTransferLength = 0; // this is a standard INQUIRY UCHAR inquiryData[ATA_INQUIRYDATA_SIZE] = {0}; PCDB cdb = SrbGetCdb(Srb); PVOID srbDataBuffer = SrbGetDataBuffer(Srb); UCHAR pathId = 0; UCHAR targetId = 0; UCHAR lun = 0; STOR_UNIT_ATTRIBUTES attributes = {0}; // report error back so that Storport may retry the command. // NOTE that a READ LOG EXT command can also reach this path for a hybrid disk. if ( (cdb != NULL) && (cdb->CDB10.OperationCode == SCSIOP_INQUIRY) && (Srb->SrbStatus != SRB_STATUS_PENDING) && (Srb->SrbStatus != SRB_STATUS_SUCCESS) && (Srb->SrbStatus != SRB_STATUS_NO_DEVICE) ) { return STOR_STATUS_UNSUCCESSFUL; } // report error if no device connected if ( ChannelExtension->DeviceExtension->DeviceParameters.AtaDeviceType == DeviceNotExist ) { Srb->SrbStatus = SRB_STATUS_NO_DEVICE; return STOR_STATUS_UNSUCCESSFUL; } SrbGetPathTargetLun(Srb, &pathId, &targetId, &lun); // Indicate the existence of a device Srb->SrbStatus = SRB_STATUS_SUCCESS; status = STOR_STATUS_SUCCESS; // make up inquiry data for ata devices AtaGenerateInquiryData(ChannelExtension, (PINQUIRYDATA)inquiryData); dataTransferLength = min(SrbGetDataTransferLength(Srb), ATA_INQUIRYDATA_SIZE); if (dataTransferLength > 0) { if (srbDataBuffer != NULL) { StorPortCopyMemory(srbDataBuffer, inquiryData, dataTransferLength); SrbSetDataTransferLength(Srb, dataTransferLength); Srb->SrbStatus = SRB_STATUS_SUCCESS; status = STOR_STATUS_SUCCESS; } else { Srb->SrbStatus = SRB_STATUS_INTERNAL_ERROR; //Srb->InternalStatus = STATUS_INSUFFICIENT_RESOURCES; status = STOR_STATUS_INSUFFICIENT_RESOURCES; } } else { // this will be a programming error. Srb->SrbStatus = SRB_STATUS_INTERNAL_ERROR; status = STOR_STATUS_INVALID_PARAMETER; } StorPortSetDeviceQueueDepth(ChannelExtension->AdapterExtension, pathId, targetId, lun, ChannelExtension->MaxPortQueueDepth); // tell port driver if D3Cold is supported or not for this device attributes.D3ColdNotSupported = (ChannelExtension->AdapterExtension->StateFlags.SupportsAcpiDSM == 0); StorPortSetUnitAttributes(ChannelExtension->AdapterExtension, (PSTOR_ADDRESS)&ChannelExtension->DeviceExtension[0].DeviceAddress, attributes); return status; } ULONG AtaInquiryRequest( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ PSTORAGE_REQUEST_BLOCK Srb, _In_ PCDB Cdb ) /* NOTE: the command should be completed after calling this function as no real command will be sent to device. */ { ULONG status = STOR_STATUS_SUCCESS; NT_ASSERT(!IsAtapiDevice(&ChannelExtension->DeviceExtension->DeviceParameters)); // Filter out all TIDs but 0 and 1 since this is an IDE interface which support up to two devices. if (SrbGetLun(Srb) != 0) { // Indicate no device found at this address. Srb->SrbStatus = SRB_STATUS_SELECTION_TIMEOUT; status = STOR_STATUS_INVALID_PARAMETER; } else if (Cdb->CDB6INQUIRY3.EnableVitalProductData == 0) { if (IsDumpMode(ChannelExtension->AdapterExtension) && !DeviceIdentificationComplete(ChannelExtension->AdapterExtension)) { // the enumeration command from dump stack. IssueIdentifyCommand(ChannelExtension, Srb); } else { status = InquiryComplete(ChannelExtension, Srb); } } else { PVOID srbDataBuffer = SrbGetDataBuffer(Srb); ULONG srbDataBufferLength = SrbGetDataTransferLength(Srb); // the INQUIRY is for VPD page AhciZeroMemory((PCHAR)srbDataBuffer, srbDataBufferLength); switch(Cdb->CDB6INQUIRY3.PageCode) { case VPD_SUPPORTED_PAGES: { // // Input buffer should be at least the size of page header plus count of supported pages, each page needs one byte. // ULONG requiredBufferSize = sizeof(VPD_SUPPORTED_PAGES_PAGE) + 6; if (srbDataBufferLength < requiredBufferSize) { Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; status = STOR_STATUS_INVALID_PARAMETER; } else { PVPD_SUPPORTED_PAGES_PAGE outputBuffer = (PVPD_SUPPORTED_PAGES_PAGE)srbDataBuffer; outputBuffer->DeviceType = ChannelExtension->DeviceExtension->DeviceParameters.ScsiDeviceType; //DIRECT_ACCESS_DEVICE; ; outputBuffer->DeviceTypeQualifier = 0; outputBuffer->PageCode = VPD_SUPPORTED_PAGES; outputBuffer->PageLength = 0x06; // supports 6 VPD pages outputBuffer->SupportedPageList[0] = VPD_SUPPORTED_PAGES; outputBuffer->SupportedPageList[1] = VPD_SERIAL_NUMBER; outputBuffer->SupportedPageList[2] = VPD_ATA_INFORMATION; outputBuffer->SupportedPageList[3] = VPD_BLOCK_LIMITS; outputBuffer->SupportedPageList[4] = VPD_BLOCK_DEVICE_CHARACTERISTICS; outputBuffer->SupportedPageList[5] = VPD_LOGICAL_BLOCK_PROVISIONING; SrbSetDataTransferLength(Srb, requiredBufferSize); Srb->SrbStatus = SRB_STATUS_SUCCESS; status = STOR_STATUS_SUCCESS; } break; } case VPD_BLOCK_LIMITS: { if (srbDataBufferLength < 0x14) { Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; status = STOR_STATUS_INVALID_PARAMETER; } else { PVPD_BLOCK_LIMITS_PAGE outputBuffer = (PVPD_BLOCK_LIMITS_PAGE)srbDataBuffer; outputBuffer->DeviceType = ChannelExtension->DeviceExtension->DeviceParameters.ScsiDeviceType; //DIRECT_ACCESS_DEVICE; ; outputBuffer->DeviceTypeQualifier = 0; outputBuffer->PageCode = VPD_BLOCK_LIMITS; // // leave outputBuffer->Descriptors[0 : 15] as '0' indicating 'not supported' for those fields. // if ( (srbDataBufferLength >= 0x24) && IsDeviceSupportsTrim(ChannelExtension) ) { // not worry about multiply overflow as max of DsmCapBlockCount is min(AHCI_MAX_TRANSFER_LENGTH / ATA_BLOCK_SIZE, 0xFFFF) // calculate how many LBA ranges can be associated with one DSM - Trim command ULONG maxLbaRangeEntryCountPerCmd = ChannelExtension->DeviceExtension[0].DeviceParameters.DsmCapBlockCount * (ATA_BLOCK_SIZE / sizeof(ATA_LBA_RANGE)); // calculate how many LBA can be associated with one DSM - Trim command ULONG maxLbaCountPerCmd = maxLbaRangeEntryCountPerCmd * MAX_ATA_LBA_RANGE_SECTOR_COUNT_VALUE; NT_ASSERT (maxLbaCountPerCmd > 0); // buffer is big enough for UNMAP information. outputBuffer->PageLength[1] = 0x3C; // must be 0x3C per spec // (16:19) MAXIMUM UNMAP LBA COUNT REVERSE_BYTES(&outputBuffer->Descriptors[16], &maxLbaCountPerCmd); // (20:23) MAXIMUM UNMAP BLOCK DESCRIPTOR COUNT REVERSE_BYTES(&outputBuffer->Descriptors[20], &maxLbaRangeEntryCountPerCmd); // (24:27) OPTIMAL UNMAP GRANULARITY // (28:31) UNMAP GRANULARITY ALIGNMENT; (28) bit7: UGAVALID //leave '0' indicates un-supported. // keep original 'Srb->DataTransferLength' value. } else { outputBuffer->PageLength[1] = 0x10; SrbSetDataTransferLength(Srb, 0x14); } Srb->SrbStatus = SRB_STATUS_SUCCESS; status = STOR_STATUS_SUCCESS; } break; } case VPD_BLOCK_DEVICE_CHARACTERISTICS: { if (srbDataBufferLength < 0x08) { Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; status = STOR_STATUS_INVALID_PARAMETER; } else { PVPD_BLOCK_DEVICE_CHARACTERISTICS_PAGE outputBuffer = (PVPD_BLOCK_DEVICE_CHARACTERISTICS_PAGE)srbDataBuffer; outputBuffer->DeviceType = ChannelExtension->DeviceExtension->DeviceParameters.ScsiDeviceType; //DIRECT_ACCESS_DEVICE; ; outputBuffer->DeviceTypeQualifier = 0; outputBuffer->PageCode = VPD_BLOCK_DEVICE_CHARACTERISTICS; outputBuffer->PageLength = 0x3C; // must be 0x3C per spec outputBuffer->MediumRotationRateMsb = (UCHAR)((ChannelExtension->DeviceExtension->IdentifyDeviceData->NominalMediaRotationRate >> 8) & 0x00FF); outputBuffer->MediumRotationRateLsb = (UCHAR)(ChannelExtension->DeviceExtension->IdentifyDeviceData->NominalMediaRotationRate & 0x00FF); outputBuffer->NominalFormFactor = (UCHAR)(ChannelExtension->DeviceExtension->IdentifyDeviceData->NominalFormFactor); Srb->SrbStatus = SRB_STATUS_SUCCESS; status = STOR_STATUS_SUCCESS; } break; } case VPD_LOGICAL_BLOCK_PROVISIONING: { if (srbDataBufferLength < 0x08) { Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; status = STOR_STATUS_INVALID_PARAMETER; } else { PVPD_LOGICAL_BLOCK_PROVISIONING_PAGE outputBuffer = (PVPD_LOGICAL_BLOCK_PROVISIONING_PAGE)srbDataBuffer; outputBuffer->DeviceType = ChannelExtension->DeviceExtension->DeviceParameters.ScsiDeviceType; //DIRECT_ACCESS_DEVICE; ; outputBuffer->DeviceTypeQualifier = 0; outputBuffer->PageCode = VPD_LOGICAL_BLOCK_PROVISIONING; outputBuffer->PageLength[1] = 0x04; // 8 bytes data in total outputBuffer->DP = 0; if (ChannelExtension->DeviceExtension->IdentifyDeviceData->AdditionalSupported.DeterministicReadAfterTrimSupported == TRUE) { outputBuffer->ANC_SUP = IsDeviceSupportsTrim(ChannelExtension) ? 1 : 0; outputBuffer->LBPRZ = ChannelExtension->DeviceExtension->IdentifyDeviceData->AdditionalSupported.ReadZeroAfterTrimSupported ? 1 : 0; } else { outputBuffer->ANC_SUP = 0; outputBuffer->LBPRZ = 0; } outputBuffer->LBPWS10 = 0; // does not support WRITE SAME(10) outputBuffer->LBPWS = 0; // does not support WRITE SAME outputBuffer->LBPU = IsDeviceSupportsTrim(ChannelExtension) ? 1 : 0; SrbSetDataTransferLength(Srb, 0x08); Srb->SrbStatus = SRB_STATUS_SUCCESS; status = STOR_STATUS_SUCCESS; } break; } case VPD_SERIAL_NUMBER: { if (srbDataBufferLength < 24) { Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; status = STOR_STATUS_INVALID_PARAMETER; } else { PVPD_SERIAL_NUMBER_PAGE outputBuffer = (PVPD_SERIAL_NUMBER_PAGE)srbDataBuffer; UCHAR i; outputBuffer->DeviceType = ChannelExtension->DeviceExtension->DeviceParameters.ScsiDeviceType; //DIRECT_ACCESS_DEVICE; ; outputBuffer->DeviceTypeQualifier = 0; outputBuffer->PageCode = VPD_SERIAL_NUMBER; outputBuffer->PageLength = 20; // 24 bytes data in total for (i = 0; i < outputBuffer->PageLength; i += 2) { REVERSE_BYTES_SHORT(&outputBuffer->SerialNumber[i], &ChannelExtension->DeviceExtension->IdentifyDeviceData->SerialNumber[i]); } SrbSetDataTransferLength(Srb, outputBuffer->PageLength + 4); Srb->SrbStatus = SRB_STATUS_SUCCESS; status = STOR_STATUS_SUCCESS; } break; } case VPD_ATA_INFORMATION: { if (srbDataBufferLength < sizeof(VPD_ATA_INFORMATION_PAGE)) { Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; status = STOR_STATUS_INVALID_PARAMETER; } else { PVPD_ATA_INFORMATION_PAGE outputBuffer = (PVPD_ATA_INFORMATION_PAGE)srbDataBuffer; ULONG vendorIdLength; ULONG len; ULONG prodLen; AhciZeroMemory((PCHAR)outputBuffer, sizeof(VPD_ATA_INFORMATION_PAGE)); outputBuffer->DeviceType = ChannelExtension->DeviceExtension->DeviceParameters.ScsiDeviceType; //DIRECT_ACCESS_DEVICE; ; outputBuffer->DeviceTypeQualifier = 0; outputBuffer->PageCode = VPD_ATA_INFORMATION; outputBuffer->PageLength[0] = 0x02; outputBuffer->PageLength[1] = 0x38; //PageLength: 0x238 - fixed value // if there is blank space in first 8 chars, use the part before blank space as VendorId for (vendorIdLength = 8; vendorIdLength > 0; vendorIdLength--) { if (ChannelExtension->DeviceExtension->DeviceParameters.VendorId[vendorIdLength - 1] == ' ') { break; } } len = min(vendorIdLength, sizeof(ChannelExtension->DeviceExtension->DeviceParameters.VendorId)); AhciFillMemory((PCHAR)outputBuffer->VendorId, 8, ' '); // if there is no blank space in first 8 chars, leave blank spaces in VendorId. Otherwise, copy the string if (len > 0 && len < 9) { StorPortCopyMemory(outputBuffer->VendorId, ChannelExtension->DeviceExtension->DeviceParameters.VendorId, len ); } prodLen = min(16, sizeof(ChannelExtension->DeviceExtension->DeviceParameters.VendorId) - len); AhciFillMemory((PCHAR)outputBuffer->ProductId, 16, ' '); StorPortCopyMemory(outputBuffer->ProductId, (ChannelExtension->DeviceExtension->DeviceParameters.VendorId + len), prodLen ); len = min(4, sizeof(ChannelExtension->DeviceExtension->DeviceParameters.RevisionId)); AhciFillMemory((PCHAR)outputBuffer->ProductRevisionLevel, 4, ' '); StorPortCopyMemory(outputBuffer->ProductRevisionLevel, ChannelExtension->DeviceExtension->DeviceParameters.RevisionId, len ); //outputBuffer->DeviceSignature -- not supported in current version of StorAHCI outputBuffer->CommandCode = IsAtaDevice(&ChannelExtension->DeviceExtension->DeviceParameters) ? IDE_COMMAND_IDENTIFY : IDE_COMMAND_ATAPI_IDENTIFY; StorPortCopyMemory(outputBuffer->IdentifyDeviceData, ChannelExtension->DeviceExtension->IdentifyDeviceData, sizeof(IDENTIFY_DEVICE_DATA)); SrbSetDataTransferLength(Srb, sizeof(VPD_ATA_INFORMATION_PAGE)); Srb->SrbStatus = SRB_STATUS_SUCCESS; status = STOR_STATUS_SUCCESS; } break; } default: Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; status = STOR_STATUS_INVALID_PARAMETER; break; } } return status; } ULONG AtaReportLunsCommand( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ PVOID Context ) /* Assumption: there will be no REPORT LUNS command received when the previous REPORT LUNS command is still in process. This function is used to respond to QDR. It send Identify (Device) command to device */ { ULONG status = STOR_STATUS_SUCCESS; PSTORAGE_REQUEST_BLOCK srb = (PSTORAGE_REQUEST_BLOCK)Context; // Filter out all TIDs but 0 since this is an AHCI interface without Port Multiplier which can only support one device. if (SrbGetLun(srb) != 0) { // Indicate no device found at this address. srb->SrbStatus = SRB_STATUS_SELECTION_TIMEOUT; status = STOR_STATUS_INVALID_PARAMETER; } else { IssueIdentifyCommand(ChannelExtension, srb); } return status; } ULONG AtaStartStopUnitRequest ( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ PSTORAGE_REQUEST_BLOCK Srb, _In_ PCDB Cdb ) { PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(Srb); UNREFERENCED_PARAMETER(ChannelExtension); //for STOP UNIT or EJECT MEDIA if (Cdb->START_STOP.Start == 0) { srbExtension->AtaFunction = ATA_FUNCTION_ATA_COMMAND; if (Cdb->START_STOP.LoadEject == 1) { SetCommandReg((&srbExtension->TaskFile.Current), IDE_COMMAND_MEDIA_EJECT); } else { SetCommandReg((&srbExtension->TaskFile.Current), IDE_COMMAND_STANDBY_IMMEDIATE); } } else { //no action required for AHCI Srb->SrbStatus = SRB_STATUS_SUCCESS; } return STOR_STATUS_SUCCESS; } ULONG AtaTestUnitReadyRequest ( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ PSTORAGE_REQUEST_BLOCK Srb ) { PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(Srb); if (IsMsnEnabled(ChannelExtension->DeviceExtension)) { // this command does not transfer data srbExtension->AtaFunction = ATA_FUNCTION_ATA_COMMAND; SetCommandReg((&srbExtension->TaskFile.Current), IDE_COMMAND_GET_MEDIA_STATUS); } else { Srb->SrbStatus = SRB_STATUS_SUCCESS; } return STOR_STATUS_SUCCESS; } ULONG AtaMediumRemovalRequest ( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ PSTORAGE_REQUEST_BLOCK Srb, _In_ PCDB Cdb ) { PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(Srb); if (IsRmfEnabled(ChannelExtension->DeviceExtension)) { // this command does not transfer data UCHAR commandReg; commandReg = (Cdb->MEDIA_REMOVAL.Prevent == 1) ? IDE_COMMAND_DOOR_LOCK : IDE_COMMAND_DOOR_UNLOCK; srbExtension->AtaFunction = ATA_FUNCTION_ATA_COMMAND; SetCommandReg((&srbExtension->TaskFile.Current), commandReg); } else { Srb->SrbStatus = SRB_STATUS_SUCCESS; } return STOR_STATUS_SUCCESS; } VOID AtaAlwaysSuccessRequestCompletion ( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ PSTORAGE_REQUEST_BLOCK Srb ) { UNREFERENCED_PARAMETER(ChannelExtension); if (SRB_STATUS(Srb->SrbStatus) != SRB_STATUS_SUCCESS) { Srb->SrbStatus = SRB_STATUS_SUCCESS; } return; } ULONG AtaFlushCommandRequest ( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ PSTORAGE_REQUEST_BLOCK Srb ) { PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(Srb); if (NoFlushDevice(ChannelExtension->DeviceExtension)) { Srb->SrbStatus = SRB_STATUS_SUCCESS; } else { // UCHAR commandReg; commandReg = Support48Bit(&ChannelExtension->DeviceExtension->DeviceParameters) ? IDE_COMMAND_FLUSH_CACHE_EXT : IDE_COMMAND_FLUSH_CACHE; srbExtension->AtaFunction = ATA_FUNCTION_ATA_FLUSH; srbExtension->CompletionRoutine = AtaAlwaysSuccessRequestCompletion; //legacy behavior: Flush Command will be always completed successfully. if ((ChannelExtension->DeviceExtension->DeviceParameters.StateFlags.SystemPoweringDown == TRUE) ) { //Final Flush goes down alone if (ChannelExtension->SlotManager.NormalQueueSlice != 0) { NT_ASSERT(FALSE); } //This is the last FLUSH. No more IO. ChannelExtension->StateFlags.NoMoreIO = TRUE; } SetCommandReg((&srbExtension->TaskFile.Current), commandReg); } return STOR_STATUS_SUCCESS; } VOID AtaPassThroughRequestCompletion ( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ PSTORAGE_REQUEST_BLOCK Srb ) { if (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_BUS_RESET) { // // Fill return registers, Storport will update Status and Error register values for reset case. // PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(Srb); if ((srbExtension->ResultBuffer != NULL) && (srbExtension->ResultBufferLength >= sizeof(DESCRIPTOR_SENSE_DATA))) { // for ATA PASS THROUGH 16 command, return Descriptor Format Sense Data, including ATA Status Return info. PDESCRIPTOR_SENSE_DATA descriptorSenseData = (PDESCRIPTOR_SENSE_DATA)srbExtension->ResultBuffer; PSCSI_SENSE_DESCRIPTOR_ATA_STATUS_RETURN ataStatus = (PSCSI_SENSE_DESCRIPTOR_ATA_STATUS_RETURN)((PUCHAR)descriptorSenseData + FIELD_OFFSET(DESCRIPTOR_SENSE_DATA, DescriptorBuffer)); AhciZeroMemory((PCHAR)srbExtension->ResultBuffer, srbExtension->ResultBufferLength); // fill sense data header, leave SenseKey, ASC, ASCQ as zero. descriptorSenseData->ErrorCode = SCSI_SENSE_ERRORCODE_DESCRIPTOR_CURRENT; descriptorSenseData->AdditionalSenseLength = sizeof(SCSI_SENSE_DESCRIPTOR_ATA_STATUS_RETURN); // fill ATA Status Return Info. ataStatus->Header.DescriptorType = SCSI_SENSE_DESCRIPTOR_TYPE_ATA_STATUS_RETURN; ataStatus->Header.AdditionalLength = 0x0C; ataStatus->Extend = Is48BitCommand(srbExtension->Flags) ? 1 : 0; ataStatus->Error = ChannelExtension->ReceivedFIS->D2hRegisterFis.Error; ataStatus->SectorCount7_0 = ChannelExtension->ReceivedFIS->D2hRegisterFis.SectorCount; ataStatus->LbaLow7_0 = ChannelExtension->ReceivedFIS->D2hRegisterFis.SectorNumber; ataStatus->LbaMid7_0 = ChannelExtension->ReceivedFIS->D2hRegisterFis.CylLow; ataStatus->LbaHigh7_0 = ChannelExtension->ReceivedFIS->D2hRegisterFis.CylHigh; ataStatus->Device = ChannelExtension->ReceivedFIS->D2hRegisterFis.Dev_Head; ataStatus->Status = ChannelExtension->ReceivedFIS->D2hRegisterFis.Status; if (Is48BitCommand(srbExtension->Flags)) { ataStatus->SectorCount15_8 = ChannelExtension->ReceivedFIS->D2hRegisterFis.SectorCount_Exp; ataStatus->LbaLow15_8 = ChannelExtension->ReceivedFIS->D2hRegisterFis.SectorNum_Exp; ataStatus->LbaMid15_8 = ChannelExtension->ReceivedFIS->D2hRegisterFis.CylLow_Exp; ataStatus->LbaHigh15_8 = ChannelExtension->ReceivedFIS->D2hRegisterFis.CylHigh_Exp; } // set flag SRB_STATUS_AUTOSENSE_VALID so that Storport will copy it back to original Sense Buffer Srb->SrbStatus |= SRB_STATUS_AUTOSENSE_VALID; } } else if (SRB_STATUS(Srb->SrbStatus) != SRB_STATUS_SUCCESS) { // keep SRB_STATUS_AUTOSENSE_VALID it's set by StorAHCI // this flag is checked by Storport to copy back SenseInfoBuffer to original sense buffer. Srb->SrbStatus = SRB_STATUS_SUCCESS | (Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID); } return; } ULONG AtaPassThroughRequest ( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ PSTORAGE_REQUEST_BLOCK Srb, _In_ PCDB Cdb ) { PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(Srb); ULONG srbFlags = SrbGetSrbFlags(Srb); UNREFERENCED_PARAMETER(ChannelExtension); // if it's a 48bit command but device doesn't support it, assert. // command issuer needs to make sure this doesn't happen. The command will be sent to device and let device fail it, so that there is ATA status and error returned to issuer. NT_ASSERT( Support48Bit(&ChannelExtension->DeviceExtension->DeviceParameters) || (Cdb->ATA_PASSTHROUGH16.Extend == 0) ); srbExtension->AtaFunction = ATA_FUNCTION_ATA_COMMAND; if (Cdb->ATA_PASSTHROUGH16.CkCond == 1) { srbExtension->Flags |= ATA_FLAGS_RETURN_RESULTS; // indicate task file content needs to be returned in SenseInfoBuffer RequestGetSrbScsiData(Srb, NULL, NULL, &srbExtension->ResultBuffer, (PUCHAR)&srbExtension->ResultBufferLength); } if (srbFlags & SRB_FLAGS_DATA_IN) { srbExtension->Flags |= ATA_FLAGS_DATA_IN; } if (srbFlags & SRB_FLAGS_DATA_OUT) { srbExtension->Flags |= ATA_FLAGS_DATA_OUT; } if ( Support48Bit(&ChannelExtension->DeviceExtension->DeviceParameters) && (Cdb->ATA_PASSTHROUGH16.Extend == 1) ) { srbExtension->Flags |= ATA_FLAGS_48BIT_COMMAND; } // ATA command taskfile srbExtension->TaskFile.Current.bFeaturesReg = Cdb->ATA_PASSTHROUGH16.Features7_0; srbExtension->TaskFile.Current.bSectorCountReg = Cdb->ATA_PASSTHROUGH16.SectorCount7_0; srbExtension->TaskFile.Current.bSectorNumberReg = Cdb->ATA_PASSTHROUGH16.LbaLow7_0; srbExtension->TaskFile.Current.bCylLowReg = Cdb->ATA_PASSTHROUGH16.LbaMid7_0; srbExtension->TaskFile.Current.bCylHighReg = Cdb->ATA_PASSTHROUGH16.LbaHigh7_0; srbExtension->TaskFile.Current.bDriveHeadReg = Cdb->ATA_PASSTHROUGH16.Device; srbExtension->TaskFile.Current.bCommandReg = Cdb->ATA_PASSTHROUGH16.Command; srbExtension->TaskFile.Previous.bFeaturesReg =Cdb->ATA_PASSTHROUGH16.Features15_8 ; srbExtension->TaskFile.Previous.bSectorCountReg = Cdb->ATA_PASSTHROUGH16.SectorCount15_8; srbExtension->TaskFile.Previous.bSectorNumberReg = Cdb->ATA_PASSTHROUGH16.LbaLow15_8; srbExtension->TaskFile.Previous.bCylLowReg = Cdb->ATA_PASSTHROUGH16.LbaMid15_8; srbExtension->TaskFile.Previous.bCylHighReg = Cdb->ATA_PASSTHROUGH16.LbaHigh15_8; srbExtension->TaskFile.Previous.bDriveHeadReg = 0; srbExtension->TaskFile.Previous.bCommandReg = 0; srbExtension->CompletionRoutine = AtaPassThroughRequestCompletion; return STOR_STATUS_SUCCESS; } ULONG ConvertUnmapBlockDescrToAtaLbaRanges( _Inout_ PUNMAP_BLOCK_DESCRIPTOR BlockDescr, _In_reads_bytes_(BufferSize) PCHAR DestBuffer, _In_ ULONG BufferSize ) /*++ Routine Description: Convert UNMAP_BLOCK_DESCRIPTOR entry to be ATA_LBA_RANGE entries. UNMAP_BLOCK_DESCRIPTOR->LbaCount is 32 bits; ATA_LBA_RANGE->SectorCount is 16 bits. it's possible that one UNMAP_BLOCK_DESCRIPTOR entry needs multiple ATA_LBA_RANGE entries. Arguments: BlockDescr - the UNMAP_BLOCK_DESCRIPTOR entry, value of fields will be updated in this function DestBuffer BufferSize Return Value: Count of ATA_LBA_RANGE entries converted. NOTE: if UNMAP_BLOCK_DESCRIPTOR->LbaCount does not reach to 0, the conversion for UNMAP_BLOCK_DESCRIPTOR entry is not completed. Further conversion is needed by calling this function again. Example: Lba - 0Bh, Length - 08h ATA: 0008_0000_0000_000B In memory(little-endian): 0B 00 00 00 00 00 - 08 00 SCSI: 0000_0000 0800_0000 0B00_0000_0000_0000 In memory(little-endian): 00 00 00 00 00 00 00 0B - 00 00 00 08 - 00 00 00 00 --*/ { ULONG convertedEntryCount; PATA_LBA_RANGE lbaRangeEntry; ULONGLONG blockDescrStartingLba; ULONG blockDescrLbaCount; convertedEntryCount = 0; lbaRangeEntry = (PATA_LBA_RANGE)DestBuffer; REVERSE_BYTES_QUAD(&blockDescrStartingLba, BlockDescr->StartingLba); REVERSE_BYTES(&blockDescrLbaCount, BlockDescr->LbaCount); // 1. fill in ATA_LBA_RANGE entries as needed while ((blockDescrLbaCount > 0) && (convertedEntryCount * sizeof(ATA_LBA_RANGE) < BufferSize)) { // ULONG sectorCount; if (blockDescrLbaCount > MAX_ATA_LBA_RANGE_SECTOR_COUNT_VALUE) { sectorCount = MAX_ATA_LBA_RANGE_SECTOR_COUNT_VALUE; } else { sectorCount = blockDescrLbaCount; } lbaRangeEntry[convertedEntryCount].StartSector = blockDescrStartingLba; lbaRangeEntry[convertedEntryCount].SectorCount = sectorCount; blockDescrStartingLba += sectorCount; blockDescrLbaCount -= sectorCount; convertedEntryCount++; } // 2. update the UNMAP Block Descriptor REVERSE_BYTES_QUAD(BlockDescr->StartingLba, &blockDescrStartingLba); REVERSE_BYTES(BlockDescr->LbaCount, &blockDescrLbaCount); return convertedEntryCount; } ULONG __inline GetDataBufferLengthForDsmCommand ( _In_ ULONG MaxLbaRangeEntryCountPerCmd, _In_ ULONG NeededLbaRangeEntryCount ) /* Input: MaxLbaRangeEntryCountPerCmd: NeededLbaRangeEntryCount: Return value: the size of buffer needed for DSM command, the size is rounded up to ATA Lba range block size (512 byes). Note that the buffer needs to be rounded up to ATA Lba Range block size (512 byes). also Note that data transfer length is much smaller than 4G, the following multiplications are safe. */ { ULONG bufferLength; if (NeededLbaRangeEntryCount >= MaxLbaRangeEntryCountPerCmd) { // 1 use the max buffer size for a DSM command bufferLength = MaxLbaRangeEntryCountPerCmd * sizeof(ATA_LBA_RANGE); } else { // 2 one single DSM command can satisfy the Unmap request bufferLength = NeededLbaRangeEntryCount * sizeof(ATA_LBA_RANGE); } // 3 buffer size round up to ATA Lba range block size (512 byes). bufferLength = ((bufferLength - 1) / ATA_BLOCK_SIZE + 1) * ATA_BLOCK_SIZE; return bufferLength; } VOID DeviceProcessTrimRequest( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ PSTORAGE_REQUEST_BLOCK Srb ) /*++ Routine Description: Process TRIM request that received from upper layer. Arguments: ChannelExtension Srb Return Value: status of the operation --*/ { PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(Srb); BOOLEAN completed = FALSE; PATA_TRIM_CONTEXT trimContext = (PATA_TRIM_CONTEXT)srbExtension->CompletionContext; PCHAR buffer = (PCHAR)srbExtension->DataBuffer; ULONG bufferLength = 0; BOOLEAN tempBlockDescrConverted = TRUE; ULONG bufferLengthUsed = 0; ULONG convertedEntryCount = 0; completed = ((Srb->SrbStatus != SRB_STATUS_PENDING) && (Srb->SrbStatus != SRB_STATUS_SUCCESS)) || (trimContext->ProcessedLbaRangeEntryCount >= trimContext->NeededLbaRangeEntryCount); if (!completed) { AhciZeroMemory(buffer, trimContext->AllocatedBufferLength); // 1. calculate the buffer size needed for DSM command bufferLength = GetDataBufferLengthForDsmCommand(trimContext->MaxLbaRangeEntryCountPerCmd, trimContext->NeededLbaRangeEntryCount - trimContext->ProcessedLbaRangeEntryCount); // 2. prepare and send DSM command // check if the Unmap request is satisfied while (trimContext->ProcessedLbaRangeEntryCount < trimContext->NeededLbaRangeEntryCount) { // when first time this function is called, trimContext->CurrentBlockDescr.LbaCount should be '0' // so that the first Block Descriptor will be copied to trimContext->CurrentBlockDescr ULONG tempBlockDescrLbaCount; REVERSE_BYTES(&tempBlockDescrLbaCount, trimContext->CurrentBlockDescr.LbaCount); tempBlockDescrConverted = (tempBlockDescrLbaCount == 0)? TRUE : FALSE; // if the previous entry conversion completed, continue the next one; // otherwise, still process the left part of the un-completed entry. if (tempBlockDescrConverted) { StorPortCopyMemory(&trimContext->CurrentBlockDescr, &trimContext->BlockDescriptors[trimContext->BlockDescrIndex], sizeof(UNMAP_BLOCK_DESCRIPTOR)); trimContext->BlockDescrIndex++; } convertedEntryCount = ConvertUnmapBlockDescrToAtaLbaRanges(&trimContext->CurrentBlockDescr, buffer + bufferLengthUsed, bufferLength - bufferLengthUsed ); trimContext->ProcessedLbaRangeEntryCount += convertedEntryCount; bufferLengthUsed += convertedEntryCount * sizeof(ATA_LBA_RANGE); // send DSM trim command when the buffer is full or all unmap entries are converted. if ( (bufferLengthUsed == bufferLength) || (trimContext->ProcessedLbaRangeEntryCount >= trimContext->NeededLbaRangeEntryCount) ) { // get ATA block count, the value is needed for setting the DSM command. ULONG transferBlockCount = bufferLength / ATA_BLOCK_SIZE; srbExtension->AtaFunction = ATA_FUNCTION_ATA_COMMAND; srbExtension->DataBuffer = buffer; srbExtension->DataTransferLength = bufferLength; // ATA command taskfile AhciZeroMemory((PCHAR)&srbExtension->TaskFile, sizeof(ATA_TASK_FILE)); srbExtension->TaskFile.Current.bFeaturesReg = IDE_DSM_FEATURE_TRIM; // For TRIM command: LBA bit (bit 6) needs to be set for Device Register; // bit 7 and bit 5 are obsolete and always set by ATAport; // bit 4 is to select device0 or device1 srbExtension->TaskFile.Current.bDriveHeadReg = 0xE0; srbExtension->TaskFile.Current.bCommandReg = IDE_COMMAND_DATA_SET_MANAGEMENT; srbExtension->TaskFile.Current.bSectorCountReg = (UCHAR)(0x00FF & transferBlockCount); srbExtension->TaskFile.Previous.bSectorCountReg = (UCHAR)(transferBlockCount >> 8); srbExtension->CompletionRoutine = DeviceProcessTrimRequest; srbExtension->CompletionContext = (PVOID)trimContext; // update the SGL to reflact the actual transfer length. srbExtension->LocalSgl.List[0].Length = bufferLength; return; } } // should not reach this path NT_ASSERT(FALSE); } else { if (buffer != NULL) { AhciFreeDmaBuffer((PVOID)ChannelExtension->AdapterExtension, trimContext->AllocatedBufferLength, buffer); } if (trimContext != NULL) { StorPortFreePool((PVOID)ChannelExtension->AdapterExtension, trimContext); } srbExtension->DataBuffer = NULL; srbExtension->CompletionContext = NULL; srbExtension->CompletionRoutine = NULL; srbExtension->AtaFunction = 0; } return; } ULONG AtaUnmapRequest ( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ PSTORAGE_REQUEST_BLOCK Srb ) { ULONG status = STOR_STATUS_SUCCESS; PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(Srb); PUNMAP_LIST_HEADER unmapList = NULL; USHORT blockDescrDataLength = 0; PATA_TRIM_CONTEXT trimContext = NULL; PCHAR buffer = NULL; // buffer allocated for DSM trim command PVOID srbDataBuffer = SrbGetDataBuffer(Srb); ULONG srbDataBufferLength = SrbGetDataTransferLength(Srb); unmapList = (PUNMAP_LIST_HEADER)srbDataBuffer; REVERSE_BYTES_SHORT(&blockDescrDataLength, unmapList->BlockDescrDataLength); if ( !IsDeviceSupportsTrim(ChannelExtension) || (srbDataBufferLength < (ULONG)(blockDescrDataLength + 8)) ) { // either trim is not supported; or the Block Descriptor Size is too big Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; status = STOR_STATUS_INVALID_PARAMETER; } else { // some preparation work before actually starting to process the request ULONG i = 0; ULONG length = 0; STOR_PHYSICAL_ADDRESS bufferPhysicalAddress; status = StorPortAllocatePool(ChannelExtension->AdapterExtension, sizeof(ATA_TRIM_CONTEXT), AHCI_POOL_TAG, (PVOID*)&trimContext); if ( (status != STOR_STATUS_SUCCESS) || (trimContext == NULL) ) { Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; if (status == STOR_STATUS_SUCCESS) { status = STOR_STATUS_INSUFFICIENT_RESOURCES; } goto Exit; } AhciZeroMemory((PCHAR)trimContext, sizeof(ATA_TRIM_CONTEXT)); trimContext->BlockDescriptors = (PUNMAP_BLOCK_DESCRIPTOR)((PCHAR)srbDataBuffer + 8); trimContext->BlockDescrCount = blockDescrDataLength / sizeof(UNMAP_BLOCK_DESCRIPTOR); // 1.1 calculate how many ATA Lba entries can be sent per DSM command // every device LBA entry takes 8 bytes. not worry about multiply overflow as max of DsmCapBlockCount is 0xFFFF trimContext->MaxLbaRangeEntryCountPerCmd = (ChannelExtension->DeviceExtension[0].DeviceParameters.DsmCapBlockCount * ATA_BLOCK_SIZE) / sizeof(ATA_LBA_RANGE); if (trimContext->MaxLbaRangeEntryCountPerCmd == 0) { // do not expect this to happen. NT_ASSERT(FALSE); Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; status = STOR_STATUS_INVALID_PARAMETER; goto Exit; } // 1.2 calculate how many ATA Lba entries needed to complete this Unmap request for (i = 0; i < trimContext->BlockDescrCount; i++) { ULONG blockDescrLbaCount; REVERSE_BYTES(&blockDescrLbaCount, trimContext->BlockDescriptors[i].LbaCount); // 1.2.1 the ATA Lba entry - SectorCount field is 16bits; the Unmap Lba entry - LbaCount field is 32bits. // following calculation shows how many ATA Lba entries should be used to represent the Unmap Lba entry. if (blockDescrLbaCount > 0) { trimContext->NeededLbaRangeEntryCount += (blockDescrLbaCount - 1) / MAX_ATA_LBA_RANGE_SECTOR_COUNT_VALUE + 1; } } // 1.3 calculate the buffer size needed for DSM command trimContext->AllocatedBufferLength = GetDataBufferLengthForDsmCommand(trimContext->MaxLbaRangeEntryCountPerCmd, trimContext->NeededLbaRangeEntryCount); if (trimContext->AllocatedBufferLength == 0) { // UNMAP without Block Descriptor is allowed, SBC spec requires to not consider this as error. Srb->SrbStatus = SRB_STATUS_SUCCESS; status = STOR_STATUS_SUCCESS; goto Exit; } // 1.4 allocate buffer, this buffer will be used to store ATA LBA Ranges for DSM command status = AhciAllocateDmaBuffer((PVOID)ChannelExtension->AdapterExtension, trimContext->AllocatedBufferLength, (PVOID*)&buffer); if ( (status != STOR_STATUS_SUCCESS) || (buffer == NULL) ) { Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; if (status == STOR_STATUS_SUCCESS) { status = STOR_STATUS_INSUFFICIENT_RESOURCES; } goto Exit; } // save values before calling DeviceProcessTrimRequest() srbExtension->AtaFunction = ATA_FUNCTION_ATA_COMMAND; srbExtension->Flags |= ATA_FLAGS_DATA_OUT; srbExtension->DataBuffer = buffer; srbExtension->DataTransferLength = trimContext->AllocatedBufferLength; srbExtension->CompletionContext = (PVOID)trimContext; bufferPhysicalAddress = StorPortGetPhysicalAddress(ChannelExtension->AdapterExtension, NULL, buffer, &length); srbExtension->LocalSgl.NumberOfElements = 1; srbExtension->LocalSgl.List[0].PhysicalAddress.LowPart = bufferPhysicalAddress.LowPart; srbExtension->LocalSgl.List[0].PhysicalAddress.HighPart = bufferPhysicalAddress.HighPart; srbExtension->LocalSgl.List[0].Length = trimContext->AllocatedBufferLength; srbExtension->Sgl = &srbExtension->LocalSgl; // process the request, this function will set itself as completion routine to send multiple DSM commands one by one. DeviceProcessTrimRequest(ChannelExtension, Srb); } Exit: // the process failed before DSM command can be sent. Free allocated resources. if (status != STOR_STATUS_SUCCESS) { if (buffer != NULL) { AhciFreeDmaBuffer((PVOID)ChannelExtension->AdapterExtension, trimContext->AllocatedBufferLength, buffer); } if (trimContext != NULL) { StorPortFreePool((PVOID)ChannelExtension->AdapterExtension, trimContext); } } return status; } ULONG AtaSecurityProtocolRequest ( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ PSTORAGE_REQUEST_BLOCK Srb, _In_ PCDB Cdb ) { PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(Srb); PCDB securityCdb = Cdb; ULONG dataLength = 0; UCHAR commandReg; UCHAR nonDataTrustedReceive = 0; if (ChannelExtension->DeviceExtension->IdentifyDeviceData->TrustedComputing.FeatureSupported == 0) { Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; } else if (securityCdb->SECURITY_PROTOCOL_IN.INC_512 == 0) { // Reject the command if INC_512 bit is set to 0. Some drivers use this answer to know the transfer size should be aligned to 512. // StorAHCI only supports the command with INC_512 bit set to 1, to avoid the complexity of handling partial sectors. Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; } else { dataLength = (securityCdb->SECURITY_PROTOCOL_IN.AllocationLength[0] << 24) | (securityCdb->SECURITY_PROTOCOL_IN.AllocationLength[0] << 16) | (securityCdb->SECURITY_PROTOCOL_IN.AllocationLength[0] << 8) | (securityCdb->SECURITY_PROTOCOL_IN.AllocationLength[0]); if (dataLength > 0xFFFF) { // ATA TRUSTED commands can only process 2 bytes of data transfter length. Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; } else { // get command to be used if ((securityCdb->SECURITY_PROTOCOL_IN.AllocationLength[3] == 0) && (securityCdb->SECURITY_PROTOCOL_IN.AllocationLength[2] == 0) && (securityCdb->SECURITY_PROTOCOL_IN.AllocationLength[1] == 0) && (securityCdb->SECURITY_PROTOCOL_IN.AllocationLength[0] == 0)) { // Non-data transfer commandReg = IDE_COMMAND_TRUSTED_NON_DATA; nonDataTrustedReceive = (Cdb->CDB10.OperationCode == SCSIOP_SECURITY_PROTOCOL_IN) ? 1 : 0; } else { NT_ASSERT((SrbGetSrbFlags(Srb) & SRB_FLAGS_UNSPECIFIED_DIRECTION) != 0); if (Cdb->CDB10.OperationCode == SCSIOP_SECURITY_PROTOCOL_IN) { commandReg = IDE_COMMAND_TRUSTED_RECEIVE_DMA; srbExtension->Flags |= ATA_FLAGS_DATA_IN; } else { commandReg = IDE_COMMAND_TRUSTED_SEND_DMA; srbExtension->Flags |= ATA_FLAGS_DATA_OUT; } srbExtension->Flags |= ATA_FLAGS_USE_DMA; } // Set up taskfile in irb. srbExtension->AtaFunction = ATA_FUNCTION_ATA_COMMAND; SetCommandReg((&srbExtension->TaskFile.Current), commandReg); SetFeaturesReg((&srbExtension->TaskFile.Current), securityCdb->SECURITY_PROTOCOL_IN.SecurityProtocol); SetSectorCount((&srbExtension->TaskFile.Current), securityCdb->SECURITY_PROTOCOL_IN.AllocationLength[3]); //low byte of transfer length SetSectorNumber((&srbExtension->TaskFile.Current), securityCdb->SECURITY_PROTOCOL_IN.AllocationLength[2]); //high byte of transfer length SetCylinderHigh((&srbExtension->TaskFile.Current), securityCdb->SECURITY_PROTOCOL_IN.SecurityProtocolSpecific[0]); //SILO_INDEX, high byte of protocol specific SetCylinderLow((&srbExtension->TaskFile.Current), securityCdb->SECURITY_PROTOCOL_IN.SecurityProtocolSpecific[1]); //FUNCTION_ID, low byte of protocol specific SetDeviceReg((&srbExtension->TaskFile.Current), nonDataTrustedReceive); } } return STOR_STATUS_SUCCESS; } UCHAR AtaMapError( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ PSTORAGE_REQUEST_BLOCK Srb, _In_ BOOLEAN FUAcommand ) /*++ Routine Description: Maps the ATA errors to SCSI and builds sense data for them Arguments: ChannelExtension Srb Return Value: SrbStatus --*/ { BOOLEAN removableMedia; SENSE_DATA senseBuffer = {0}; ULONG length = sizeof(SENSE_DATA); PVOID srbSenseBuffer = NULL; UCHAR srbSenseBufferLength = 0; PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(Srb); RequestGetSrbScsiData(Srb, NULL, NULL, &srbSenseBuffer, &srbSenseBufferLength); if (IsReturnResults(srbExtension->Flags)) { // There is already something in the SenseInfoBuffer or this is an ATA PASS THRU. return Srb->SrbStatus; } // 1. special interpretion for FUA command if (FUAcommand == TRUE) { //if the first FUA command succeeds, remember so future failures don't disable FUA commands //if the first FUA command fails, leverage the class driver to not send anymore FUA commands if (ChannelExtension->DeviceExtension->DeviceParameters.StateFlags.FuaSucceeded == 0) { if (Srb->SrbStatus == SRB_STATUS_SUCCESS) { ChannelExtension->DeviceExtension->DeviceParameters.StateFlags.FuaSucceeded = 1; } else if (Srb->SrbStatus == SRB_STATUS_ERROR) { // Srb->SrbStatus = SRB_STATUS_ERROR; Srb->SrbStatus |= SRB_STATUS_AUTOSENSE_VALID; SrbSetScsiStatus(Srb, SCSISTAT_CHECK_CONDITION); senseBuffer.ErrorCode = SCSI_SENSE_ERRORCODE_FIXED_CURRENT; senseBuffer.Valid = 1; senseBuffer.AdditionalSenseLength = 0xb; senseBuffer.SenseKey = SCSI_SENSE_ILLEGAL_REQUEST; senseBuffer.AdditionalSenseCode = SCSI_ADSENSE_INVALID_CDB; senseBuffer.AdditionalSenseCodeQualifier = 0; } } } if (Srb->SrbStatus != SRB_STATUS_ERROR) { // non device errors. Don't care ChannelExtension->DeviceExtension[0].IoRecord.SuccessCount++; return Srb->SrbStatus; } // 2. general process removableMedia = IsRemovableMedia(&ChannelExtension->DeviceExtension->DeviceParameters); if (senseBuffer.Valid == 0) { // senseBuffer is not set yet, start ... if (srbExtension->AtaError & IDE_ERROR_CRC_ERROR) { // bit 7: Interface CRC error Srb->SrbStatus = SRB_STATUS_PARITY_ERROR; senseBuffer.ErrorCode = SCSI_SENSE_ERRORCODE_FIXED_CURRENT; senseBuffer.Valid = 1; senseBuffer.AdditionalSenseLength = 0xb; senseBuffer.SenseKey = SCSI_SENSE_HARDWARE_ERROR; senseBuffer.AdditionalSenseCode = SCSI_ADSENSE_LUN_COMMUNICATION; senseBuffer.AdditionalSenseCodeQualifier = SCSI_SESNEQ_COMM_CRC_ERROR; ChannelExtension->DeviceExtension[0].IoRecord.CrcErrorCount++; } else if (srbExtension->AtaError & IDE_ERROR_DATA_ERROR) { // bit 6: Uncorrectable Error Srb->SrbStatus = SRB_STATUS_ERROR; senseBuffer.ErrorCode = SCSI_SENSE_ERRORCODE_FIXED_CURRENT; senseBuffer.Valid = 1; senseBuffer.AdditionalSenseLength = 0xb; senseBuffer.SenseKey = removableMedia ? SCSI_SENSE_DATA_PROTECT : SCSI_SENSE_MEDIUM_ERROR ; senseBuffer.AdditionalSenseCode = 0; senseBuffer.AdditionalSenseCodeQualifier = 0; if (removableMedia) { ChannelExtension->DeviceExtension[0].IoRecord.OtherErrorCount++; } else { ChannelExtension->DeviceExtension[0].IoRecord.MediaErrorCount++; } } else if (srbExtension->AtaError & IDE_ERROR_MEDIA_CHANGE) { // bit 5: Media Changed (legacy) Srb->SrbStatus = SRB_STATUS_ERROR; senseBuffer.ErrorCode = SCSI_SENSE_ERRORCODE_FIXED_CURRENT; senseBuffer.Valid = 1; senseBuffer.AdditionalSenseLength = 0xb; senseBuffer.SenseKey = SCSI_SENSE_UNIT_ATTENTION; senseBuffer.AdditionalSenseCode = SCSI_ADSENSE_MEDIUM_CHANGED; senseBuffer.AdditionalSenseCodeQualifier = 0; ChannelExtension->DeviceExtension[0].IoRecord.OtherErrorCount++; } else if (srbExtension->AtaError & IDE_ERROR_ID_NOT_FOUND) { // bit 4: ID Not Found Srb->SrbStatus = SRB_STATUS_ERROR; senseBuffer.ErrorCode = SCSI_SENSE_ERRORCODE_FIXED_CURRENT; senseBuffer.Valid = 1; senseBuffer.AdditionalSenseLength = 0xb; senseBuffer.SenseKey = SCSI_SENSE_ILLEGAL_REQUEST; senseBuffer.AdditionalSenseCode = SCSI_ADSENSE_ILLEGAL_BLOCK; senseBuffer.AdditionalSenseCodeQualifier = 0; ChannelExtension->DeviceExtension[0].IoRecord.IllegalCommandCount++; } else if (srbExtension->AtaError & IDE_ERROR_MEDIA_CHANGE_REQ) { // bit 3: Media Change Request (legacy) Srb->SrbStatus = SRB_STATUS_ERROR; senseBuffer.ErrorCode = SCSI_SENSE_ERRORCODE_FIXED_CURRENT; senseBuffer.Valid = 1; senseBuffer.AdditionalSenseLength = 0xb; senseBuffer.SenseKey = SCSI_SENSE_UNIT_ATTENTION; senseBuffer.AdditionalSenseCode = SCSI_ADSENSE_OPERATOR_REQUEST; senseBuffer.AdditionalSenseCodeQualifier = SCSI_SENSEQ_MEDIUM_REMOVAL; ChannelExtension->DeviceExtension[0].IoRecord.OtherErrorCount++; } else if (srbExtension->AtaError & IDE_ERROR_COMMAND_ABORTED) { // bit 2: Command Aborted Srb->SrbStatus = SRB_STATUS_ABORTED; senseBuffer.ErrorCode = SCSI_SENSE_ERRORCODE_FIXED_CURRENT; senseBuffer.Valid = 1; senseBuffer.AdditionalSenseLength = 0xb; senseBuffer.SenseKey = SCSI_SENSE_ABORTED_COMMAND; senseBuffer.AdditionalSenseCode = 0; senseBuffer.AdditionalSenseCodeQualifier = 0; ChannelExtension->DeviceExtension[0].IoRecord.AbortedCommandCount++; } else if (srbExtension->AtaError & IDE_ERROR_END_OF_MEDIA) { // bit 1: End of Media (legacy) Srb->SrbStatus = SRB_STATUS_ERROR; senseBuffer.ErrorCode = SCSI_SENSE_ERRORCODE_FIXED_CURRENT; senseBuffer.Valid = 1; senseBuffer.AdditionalSenseLength = 0xb; senseBuffer.SenseKey = SCSI_SENSE_NOT_READY; senseBuffer.AdditionalSenseCode = SCSI_ADSENSE_NO_MEDIA_IN_DEVICE; senseBuffer.AdditionalSenseCodeQualifier = 0; ChannelExtension->DeviceExtension[0].IoRecord.EndofMediaCount++; } else if (srbExtension->AtaError & IDE_ERROR_ADDRESS_NOT_FOUND) { // bit 0: Media Error (legacy) Srb->SrbStatus = SRB_STATUS_ERROR; senseBuffer.ErrorCode = SCSI_SENSE_ERRORCODE_FIXED_CURRENT; senseBuffer.Valid = 1; senseBuffer.AdditionalSenseLength = 0xb; senseBuffer.SenseKey = removableMedia ? SCSI_SENSE_DATA_PROTECT : SCSI_SENSE_MEDIUM_ERROR ; senseBuffer.AdditionalSenseCode = 0; senseBuffer.AdditionalSenseCodeQualifier = 0; if (removableMedia) { ChannelExtension->DeviceExtension[0].IoRecord.OtherErrorCount++; } else { ChannelExtension->DeviceExtension[0].IoRecord.MediaErrorCount++; } /* // // The following translation is provided as a guideline. // } else if (IsReadWriteCommand(Srb) && (srbExtension->AtaStatus & IDE_STATUS_DEVICE_FAULT)) { // // If this is a read or write command and the Device Fault (DF) // bit in the Status is set, then translate this to a Hardware // Error, Internal Target Failure, according to SAT-3. // Srb->SrbStatus = SRB_STATUS_ERROR; senseBuffer.ErrorCode = SCSI_SENSE_ERRORCODE_FIXED_CURRENT; senseBuffer.Valid = 1; senseBuffer.AdditionalSenseLength = 0xb; senseBuffer.SenseKey = SCSI_SENSE_HARDWARE_ERROR; senseBuffer.AdditionalSenseCode = SCSI_ADSENSE_INTERNAL_TARGET_FAILURE; senseBuffer.AdditionalSenseCodeQualifier = 0; */ } else { Srb->SrbStatus = SRB_STATUS_ERROR; ChannelExtension->DeviceExtension[0].IoRecord.OtherErrorCount++; } } if ( (senseBuffer.Valid == 1) && (srbSenseBuffer != NULL) ) { if ( (srbSenseBufferLength > 0) && (srbSenseBufferLength < length) ) { length = srbSenseBufferLength; } NT_ASSERT(length > 0); StorPortCopyMemory(srbSenseBuffer, (PVOID)&senseBuffer, length); Srb->SrbStatus |= SRB_STATUS_AUTOSENSE_VALID; SrbSetScsiStatus(Srb, SCSISTAT_CHECK_CONDITION); } return Srb->SrbStatus; } VOID CopyField( _Out_writes_bytes_(Count+1) PUCHAR Destination, _In_reads_bytes_(Count) PUCHAR Source, _In_ ULONG Count, _In_ UCHAR Change ) /*++ Routine Description: This routine will copy Count string bytes from Source to Destination. If it finds a null byte in the Source it will translate that and any subsequent bytes into Change. It will also replace non-printable characters with the specified character. Arguments: Destination - the location to copy bytes Source - the location to copy bytes from Count - the number of bytes to be copied Return Value: none Notes: This routine will add a NULL char at Destination[Count]. --*/ { ULONG i = 0; BOOLEAN pastEnd = FALSE; for(i = 0; i < Count; i++) { if(!pastEnd) { if(Source[i] == 0) { pastEnd = TRUE; Destination[i] = Change; } else if ((Source[i] <= ' ') || (Source[i] > 0x7f) || (Source[i] == ',')) { Destination[i] = Change; } else { Destination[i] = Source[i]; } } else { Destination[i] = Change; } } Destination[i] = '\0'; return; } VOID FormatAtaId ( _Out_writes_bytes_(CharCount + 1) PUCHAR Destination, _In_reads_bytes_(CharCount) PUCHAR Source, _In_ ULONG CharCount ) /*++ Constructs and formats ATA string. Used for ModelNumber, FirmwareRevision and SerialNumber --*/ { NT_ASSERT(CharCount > 0); Destination[0] = '\0'; CopyField(Destination, Source, CharCount, ' '); ByteSwap(Destination, CharCount); // This will null terminate the string RemoveTrailingBlanks(Destination, CharCount + 1); return; } VOID DeviceInitAtaIds( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ PIDENTIFY_DEVICE_DATA IdentifyDeviceData ) { FormatAtaId(ChannelExtension->DeviceExtension->DeviceParameters.VendorId, IdentifyDeviceData->ModelNumber, sizeof(ChannelExtension->DeviceExtension->DeviceParameters.VendorId) - 1 ); FormatAtaId(ChannelExtension->DeviceExtension->DeviceParameters.RevisionId, IdentifyDeviceData->FirmwareRevision, sizeof(ChannelExtension->DeviceExtension->DeviceParameters.RevisionId) - 1 ); FormatAtaId(ChannelExtension->DeviceExtension->DeviceParameters.SerialNumber, IdentifyDeviceData->SerialNumber, sizeof(ChannelExtension->DeviceExtension->DeviceParameters.SerialNumber) - 1 ); return; } VOID FormatAtapiVendorId( _In_ PINQUIRYDATA InquiryData, _Out_writes_bytes_(Length) PUCHAR VendorId, _In_ ULONG Length ) /*++ Routine Description: Constructs and formats the Vendor Id from InquiryData Arguments: InquiryData - the InquiryData from the device VendorId - destination buffer Length - Length of the destination buffer Return Value: None. --*/ { ULONG index; ULONG bytesLeft; VendorId[0] = '\0'; bytesLeft = Length; if (bytesLeft > 8) { CopyField( VendorId, InquiryData->VendorId, 8, ' ' ); NT_ASSERT(VendorId[8] == '\0'); VendorId[8] = ' '; index = RemoveTrailingBlanks(VendorId, 9); if (index < Length) { NT_ASSERT(VendorId[index] == '\0'); VendorId[index] = ' '; VendorId += index + 1; bytesLeft -= (index + 1); } } if (bytesLeft > 16) { // At this point, VendorId points a buffer that has at least 16 bytes #pragma warning (suppress: 6386) CopyField( VendorId, InquiryData->ProductId, 16, ' ' ); NT_ASSERT(VendorId[16] == '\0'); VendorId[16] = ' '; RemoveTrailingBlanks(VendorId, 17); } return; } VOID FormatAtapiRevisionId( _In_ PINQUIRYDATA InquiryData, _Out_writes_bytes_(Length) PUCHAR RevisionId, _In_ ULONG Length ) /*++ Routine Description: Constructs and formats the Revision Id from InquiryData Arguments: InquiryData - the InquiryDatadata from the device RevisionId - destination buffer Length - Length of the destination buffer Return Value: None. --*/ { RevisionId[0] = '\0'; if (Length > 4) { CopyField( RevisionId, InquiryData->ProductRevisionLevel, 4, ' ' ); NT_ASSERT(RevisionId[4] == '\0'); RevisionId[4] = ' '; RemoveTrailingBlanks(RevisionId, 5); } return; } VOID DeviceInitAtapiIds( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ PINQUIRYDATA InquiryData ) { FormatAtapiVendorId(InquiryData, ChannelExtension->DeviceExtension->DeviceParameters.VendorId, sizeof(ChannelExtension->DeviceExtension->DeviceParameters.VendorId) ); FormatAtapiRevisionId(InquiryData, ChannelExtension->DeviceExtension->DeviceParameters.RevisionId, sizeof(ChannelExtension->DeviceExtension->DeviceParameters.RevisionId) ); return; } VOID UpdateDeviceParameters( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension ) /*++ Routine Description: Central place to update device related information after device enumeration Assumption - IDENTIFY DATA has been retrieved from device Arguments: ChannelExtension - Return Value: None. --*/ { PATA_DEVICE_PARAMETERS deviceParameters = &ChannelExtension->DeviceExtension->DeviceParameters; PIDENTIFY_DEVICE_DATA identifyDeviceData = ChannelExtension->DeviceExtension->IdentifyDeviceData; //PINQUIRYDATA inquiryData = (PINQUIRYDATA)ChannelExtension->DeviceExtension->InquiryData; //1. re-initialize device specific information to avoid the values being reused after device switched. ChannelExtension->StateFlags.NCQ_Activated = 0; ChannelExtension->StateFlags.NCQ_Succeeded = 0; ChannelExtension->StateFlags.HybridInfoEnabledOnHiberFile = 0; ChannelExtension->DeviceExtension->HybridCachingMediumEnableRefs = 0; AhciZeroMemory((PCHAR)&ChannelExtension->DeviceExtension->SupportedGPLPages, sizeof(ATA_SUPPORTED_GPL_PAGES)); if (IsAtapiDevice(deviceParameters)) { deviceParameters->MaximumLun = 0; // following two fields are not used for ATAPI device. //deviceParameters->ScsiDeviceType = inquiryData->DeviceType; //deviceParameters->StateFlags.RemovableMedia = inquiryData->RemovableMedia; deviceParameters->MaxDeviceQueueDepth = 1; deviceParameters->AddressTranslation = UnknownMode; } else { // this is an ATA device BOOLEAN isBigLba = FALSE; deviceParameters->MaximumLun = 0; deviceParameters->ScsiDeviceType = DIRECT_ACCESS_DEVICE; deviceParameters->StateFlags.RemovableMedia = identifyDeviceData->GeneralConfiguration.RemovableMedia; deviceParameters->MaxDeviceQueueDepth = min(ChannelExtension->MaxPortQueueDepth, (UCHAR)identifyDeviceData->QueueDepth); if (identifyDeviceData->CommandSetSupport.WriteFua && identifyDeviceData->CommandSetActive.WriteFua) { // FUA support deviceParameters->StateFlags.FuaSupported = 1; } // ATA/ATAPI Revision 6.0 or later trust the bit in word 49 in the identify data. // by spec this shall be set to 1 NT_ASSERT(identifyDeviceData->Capabilities.LbaSupported == 0x1); // check if it supports 48 bit LBA if (identifyDeviceData->CommandSetSupport.BigLba && identifyDeviceData->CommandSetActive.BigLba) { LARGE_INTEGER tempLba; tempLba.LowPart = identifyDeviceData->Max48BitLBA[0]; tempLba.HighPart = identifyDeviceData->Max48BitLBA[1]; // Some disk drives seem to set the above bits but // fail to respond to 48 bit LBA commands. So enable // 48 bit lba only if it is absolutely necessary if (tempLba.QuadPart >= MAX_28BIT_LBA) { isBigLba = TRUE; } } if (isBigLba) { deviceParameters->AddressTranslation = Lba48BitMode; } else { deviceParameters->AddressTranslation = LbaMode; } //2.1 Negotiate NCQ features if (IsNCQSupported(ChannelExtension)) { if (!IsDumpMode(ChannelExtension->AdapterExtension)) { ChannelExtension->StateFlags.NCQ_Activated = 1; deviceParameters->AddressTranslation = Lba48BitMode; deviceParameters->StateFlags.FuaSupported = 1; } else if (IsDumpHiberMode(ChannelExtension->AdapterExtension) && IsDeviceHybridInfoEnabled(ChannelExtension)) { // allow NCQ and Hybrid Info conveyed by NCQ Write command during hibernation. ChannelExtension->StateFlags.NCQ_Activated = 1; ChannelExtension->StateFlags.HybridInfoEnabledOnHiberFile = 1; deviceParameters->AddressTranslation = Lba48BitMode; deviceParameters->StateFlags.FuaSupported = 1; } } } DeviceInitAtaIds(ChannelExtension, identifyDeviceData); SelectDeviceGeometry(ChannelExtension, deviceParameters, identifyDeviceData); return; } ULONG IOCTLtoATA( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ PSTORAGE_REQUEST_BLOCK Srb ) /* Note: If there is a need to send a command to device, the translation routine shall set appropriate value to srbExtension->AtaFunction. For example: srbExtension->AtaFunction = ATA_FUNCTION_ATA_COMMAND; Not setting this field can cause the Srb being completed earlier than expected. */ { PSRB_IO_CONTROL srbControl; ULONG status; PVOID srbDataBuffer = SrbGetDataBuffer(Srb); ULONG srbDataBufferLength = SrbGetDataTransferLength(Srb); srbControl = (PSRB_IO_CONTROL)srbDataBuffer; status = STOR_STATUS_SUCCESS; switch (srbControl->ControlCode) { case IOCTL_SCSI_MINIPORT_SMART_VERSION: status = SmartVersion (ChannelExtension, Srb); break; case IOCTL_SCSI_MINIPORT_IDENTIFY: status = SmartIdentifyData (ChannelExtension, Srb); break; case IOCTL_SCSI_MINIPORT_READ_SMART_ATTRIBS: case IOCTL_SCSI_MINIPORT_READ_SMART_THRESHOLDS: case IOCTL_SCSI_MINIPORT_ENABLE_SMART: case IOCTL_SCSI_MINIPORT_DISABLE_SMART: case IOCTL_SCSI_MINIPORT_RETURN_STATUS: case IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTOSAVE: case IOCTL_SCSI_MINIPORT_SAVE_ATTRIBUTE_VALUES: case IOCTL_SCSI_MINIPORT_EXECUTE_OFFLINE_DIAGS: case IOCTL_SCSI_MINIPORT_ENABLE_DISABLE_AUTO_OFFLINE: case IOCTL_SCSI_MINIPORT_READ_SMART_LOG: case IOCTL_SCSI_MINIPORT_WRITE_SMART_LOG: status = SmartGeneric (ChannelExtension, Srb); break; case IOCTL_SCSI_MINIPORT_NVCACHE: { // general NVCACHE parameter validation PNVCACHE_REQUEST_BLOCK nvCacheRequest; if ( srbDataBufferLength < (sizeof(SRB_IO_CONTROL) + sizeof(NVCACHE_REQUEST_BLOCK)) ) { Srb->SrbStatus = SRB_STATUS_BAD_SRB_BLOCK_LENGTH; status = STOR_STATUS_BUFFER_TOO_SMALL; break; } nvCacheRequest = (PNVCACHE_REQUEST_BLOCK)(srbControl + 1); if ( ((srbDataBufferLength - sizeof(SRB_IO_CONTROL) - sizeof(NVCACHE_REQUEST_BLOCK)) < nvCacheRequest->DataBufSize) || (nvCacheRequest->DataBufSize > AHCI_MAX_TRANSFER_LENGTH) ) { nvCacheRequest->NRBStatus = NRB_INVALID_PARAMETER; Srb->SrbStatus = SRB_STATUS_BAD_SRB_BLOCK_LENGTH; status = STOR_STATUS_INVALID_PARAMETER; break; } // process NVCACHE request according to function switch (nvCacheRequest->Function) { default: status = NVCacheGeneric (ChannelExtension, Srb); break; } break; } case IOCTL_SCSI_MINIPORT_DSM_GENERAL: status = DsmGeneralIoctlProcess(ChannelExtension, Srb); break; case IOCTL_SCSI_MINIPORT_HYBRID: status = HybridIoctlProcess(ChannelExtension, Srb); break; default: Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; status = STOR_STATUS_INVALID_PARAMETER; break; } return status; } ULONG SmartVersion( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ PSTORAGE_REQUEST_BLOCK Srb ) { PGETVERSIONINPARAMS versionParameters; if (sizeof(SRB_IO_CONTROL) + sizeof(GETVERSIONINPARAMS) > SrbGetDataTransferLength(Srb)) { NT_ASSERT(FALSE); Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; return STOR_STATUS_BUFFER_TOO_SMALL; } versionParameters = (PGETVERSIONINPARAMS)(((PUCHAR)SrbGetDataBuffer(Srb)) + sizeof(SRB_IO_CONTROL)); // // Version and revision per SMART 1.03 // versionParameters->bVersion = 1; versionParameters->bRevision = 1; versionParameters->bReserved = 0; // // Indicate that support for IDE IDENTIFY, ATAPI IDENTIFY and SMART commands. // versionParameters->fCapabilities = (CAP_ATA_ID_CMD | CAP_ATAPI_ID_CMD | CAP_SMART_CMD); // // the bIDEDeviceMap is a bit map, with the bits defined as follows // bit 0 - IDE drive as device0 on Primary channel // bit 1 - IDE drive as device1 on Primary channel // bit 2 - IDE drive as device0 on Secondary channel // bit 3 - IDE drive as device1 on Secondary Channel // bit 4 - ATAPI drive as device0 on Primary Channel // bit 5 - ATAPI drive as device1 on Primary Channel // bit 6 - ATAPI drive as device0 on secondary Channel // bit 7 - ATAPI drive as device1 on secondary Channel // // since this doesn't apply to SATA, we can only fill in the fields pertinent to this channel. // if (IsAtapiDevice(&ChannelExtension->DeviceExtension->DeviceParameters)) { versionParameters->bIDEDeviceMap = (1 << 4); } else { versionParameters->bIDEDeviceMap = (1 << 0); } Srb->SrbStatus = SRB_STATUS_SUCCESS; return STOR_STATUS_SUCCESS; } BOOLEAN FillClippedSGL( _In_ PSTOR_SCATTER_GATHER_LIST SourceSgl, _Inout_ PSTOR_SCATTER_GATHER_LIST LocalSgl, _In_ ULONG BytesLeft, _In_ ULONG BytesNeeded ) /* This routine cuts the beginning 'BytesLeft' from 'SourceSgl' and copy the left ranges to 'LocalSgl'. BytesLeft - the bytes count from starting of SourceSgl that should not be used. BytesNeeded - the number of bytes to be needed for data transfer. This routine is typically called by IOCTL to ATA command translaton, which the buffer contains some control information at the beginning. The real buffer for data transfer is following the controll information. */ { ULONG i, j; if ( (SourceSgl == NULL) || (LocalSgl == NULL) || (BytesNeeded == 0) ) { return FALSE; } j = 0; for (i = 0; (i < SourceSgl->NumberOfElements) && (BytesNeeded > 0); i++) { if (BytesLeft > 0 ) { if (BytesLeft < SourceSgl->List[i].Length) { //Shrink this element LocalSgl->List[j].PhysicalAddress.LowPart = SourceSgl->List[i].PhysicalAddress.LowPart + BytesLeft; LocalSgl->List[j].PhysicalAddress.HighPart = SourceSgl->List[i].PhysicalAddress.HighPart; LocalSgl->List[j].Length = SourceSgl->List[i].Length - BytesLeft; if (LocalSgl->List[j].Length < BytesNeeded) { //Not done. Need more elements. BytesNeeded -= LocalSgl->List[j].Length; } else { //Done! Get enough elements. LocalSgl->List[j].Length = BytesNeeded; BytesNeeded = 0; } BytesLeft = 0; j++; } else if (BytesLeft == SourceSgl->List[i].Length) { //Done! Cut off this element BytesLeft = 0; } else { //notDone. Cut off this element and shrink bytesLeft BytesLeft = BytesLeft - SourceSgl->List[i].Length; } } else { //no modification necessary. copy straight over. LocalSgl->List[j].PhysicalAddress.LowPart = SourceSgl->List[i].PhysicalAddress.LowPart; LocalSgl->List[j].PhysicalAddress.HighPart = SourceSgl->List[i].PhysicalAddress.HighPart; LocalSgl->List[j].Length = SourceSgl->List[i].Length; if (LocalSgl->List[j].Length < BytesNeeded) { //Not done. Need more elements. BytesNeeded -= LocalSgl->List[j].Length; } else { //Done! Get enough elements. LocalSgl->List[j].Length = BytesNeeded; BytesNeeded = 0; } j++; } } //record the number of elements left LocalSgl->NumberOfElements = j; if ((LocalSgl->NumberOfElements > 33) || (BytesLeft != 0) || (BytesNeeded != 0)) { return FALSE; } else { return TRUE; } } ULONG SmartIdentifyData( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ PSTORAGE_REQUEST_BLOCK Srb ) { PSENDCMDOUTPARAMS outParams; PAHCI_SRB_EXTENSION srbExtension; PUCHAR buffer; //to make the pointer arithmatic easier ULONG srbDataBufferLength = SrbGetDataTransferLength(Srb); buffer = (PUCHAR)SrbGetDataBuffer(Srb) + sizeof(SRB_IO_CONTROL); #pragma warning (suppress: 28930) // Temporarily suppress warning due to OACR false positive. outParams = (PSENDCMDOUTPARAMS)buffer; if ( srbDataBufferLength < (sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDOUTPARAMS) - 1 + sizeof(IDENTIFY_DEVICE_DATA)) ) { NT_ASSERT(FALSE); if ( srbDataBufferLength >= sizeof(SRB_IO_CONTROL) + RTL_SIZEOF_THROUGH_FIELD(SENDCMDOUTPARAMS, DriverStatus) ) { outParams->DriverStatus.bDriverError = SMART_INVALID_BUFFER; outParams->DriverStatus.bIDEError = 0; } Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; return STOR_STATUS_BUFFER_TOO_SMALL; } //ATAPI devices cannot support SMART (see ATA8-ACS2 r2 Section 7.19 Table 40 word 82 bit 0) if (IsAtapiDevice(&ChannelExtension->DeviceExtension->DeviceParameters)) { NT_ASSERT(FALSE); outParams->DriverStatus.bDriverError = SMART_INVALID_DRIVE; outParams->DriverStatus.bIDEError = 0; Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; return STOR_STATUS_INVALID_DEVICE_REQUEST; } //1 Fills in the local SGL srbExtension = GetSrbExtension(Srb); srbExtension->AtaFunction = ATA_FUNCTION_ATA_IDENTIFY; srbExtension->Flags |= ATA_FLAGS_DATA_IN; srbExtension->CompletionRoutine = AhciPortSmartCompletion; //setup TaskFile AhciZeroMemory((PCHAR) &srbExtension->TaskFile, sizeof(ATA_TASK_FILE)); srbExtension->TaskFile.Current.bDriveHeadReg = 0xA0; srbExtension->TaskFile.Current.bCommandReg = IDE_COMMAND_IDENTIFY; if (! FillClippedSGL( StorPortGetScatterGatherList(ChannelExtension->AdapterExtension, (PSCSI_REQUEST_BLOCK)Srb), (PSTOR_SCATTER_GATHER_LIST) &srbExtension->LocalSgl, sizeof(SRB_IO_CONTROL) + (sizeof(SENDCMDOUTPARAMS) - 1), sizeof(IDENTIFY_DEVICE_DATA) ) ) { Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; return STOR_STATUS_BUFFER_TOO_SMALL; } else { srbExtension->Sgl = &srbExtension->LocalSgl; srbExtension->DataTransferLength = sizeof(IDENTIFY_DEVICE_DATA); } Srb->SrbStatus = SRB_STATUS_PENDING; return STOR_STATUS_SUCCESS; } ULONG SmartGeneric( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ PSTORAGE_REQUEST_BLOCK Srb ) { PSENDCMDOUTPARAMS outParams; PSENDCMDINPARAMS inParams; PAHCI_SRB_EXTENSION srbExtension; PUCHAR buffer;//to make the pointer arithmatic easier ULONG srbDataBufferLength; buffer = (PUCHAR)SrbGetDataBuffer(Srb) + sizeof(SRB_IO_CONTROL); inParams = (PSENDCMDINPARAMS ) buffer; outParams = (PSENDCMDOUTPARAMS) buffer; srbDataBufferLength = SrbGetDataTransferLength(Srb); if (inParams->irDriveRegs.bCommandReg == SMART_CMD) { srbExtension = GetSrbExtension(Srb); switch (inParams->irDriveRegs.bFeaturesReg) { case READ_ATTRIBUTES: case READ_THRESHOLDS: case SMART_READ_LOG: case SMART_WRITE_LOG: if (srbDataBufferLength <= (sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDOUTPARAMS) - 1)) { if (srbDataBufferLength >= (sizeof(SRB_IO_CONTROL) + RTL_SIZEOF_THROUGH_FIELD(SENDCMDOUTPARAMS, DriverStatus))) { outParams->DriverStatus.bDriverError = SMART_INVALID_BUFFER; outParams->DriverStatus.bIDEError = 0; } Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; return STOR_STATUS_BUFFER_TOO_SMALL; } //Setup the outbuffer to hold the data transfered //Ensure the PRDT will get set up if (inParams->irDriveRegs.bFeaturesReg == SMART_WRITE_LOG) { srbExtension->Flags |= ATA_FLAGS_DATA_OUT; } else { srbExtension->Flags |= ATA_FLAGS_DATA_IN; } //Create the SGL to use to set up the PRDT if (! FillClippedSGL( StorPortGetScatterGatherList(ChannelExtension->AdapterExtension, (PSCSI_REQUEST_BLOCK)Srb), (PSTOR_SCATTER_GATHER_LIST) &srbExtension->LocalSgl, sizeof(SRB_IO_CONTROL) + (sizeof(SENDCMDOUTPARAMS) - 1), srbDataBufferLength - sizeof(SRB_IO_CONTROL) - (sizeof(SENDCMDOUTPARAMS) - 1) ) ) { Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; return STOR_STATUS_BUFFER_TOO_SMALL; } else { srbExtension->Sgl = &srbExtension->LocalSgl; srbExtension->DataTransferLength = srbDataBufferLength - sizeof(SRB_IO_CONTROL) - (sizeof(SENDCMDOUTPARAMS) - 1); } break; case RETURN_SMART_STATUS: if (srbDataBufferLength < (sizeof(SRB_IO_CONTROL) + sizeof(SENDCMDOUTPARAMS) - 1 + 8)) { if (srbDataBufferLength >= (sizeof(SRB_IO_CONTROL) + RTL_SIZEOF_THROUGH_FIELD(SENDCMDOUTPARAMS, DriverStatus))) { outParams->DriverStatus.bDriverError = SMART_INVALID_BUFFER; outParams->DriverStatus.bIDEError = 0; } Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; return STOR_STATUS_BUFFER_TOO_SMALL; } //Setup outbuffer to recieve the return task file srbExtension->ResultBuffer = (PVOID)outParams->bBuffer; srbExtension->ResultBufferLength = 8; srbExtension->Flags |= ATA_FLAGS_RETURN_RESULTS; //there is no data transfer. break; case EXECUTE_OFFLINE_DIAGS: //Allow only the non-captive tests, for now. if ((inParams->irDriveRegs.bSectorNumberReg == SMART_SHORT_SELFTEST_CAPTIVE) || (inParams->irDriveRegs.bSectorNumberReg == SMART_EXTENDED_SELFTEST_CAPTIVE)) { Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; return STOR_STATUS_INVALID_DEVICE_REQUEST; } //there is no data transfer. //Nothing to do for these break; case ENABLE_SMART: case DISABLE_SMART: case SAVE_ATTRIBUTE_VALUES: case ENABLE_DISABLE_AUTOSAVE: case ENABLE_DISABLE_AUTO_OFFLINE: //there is no data transfer. //Nothing to do for these break; default: Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; return STOR_STATUS_INVALID_DEVICE_REQUEST; break; } } else { //only smart commands are supported Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; return STOR_STATUS_INVALID_DEVICE_REQUEST; } //If we made it this far without changing the status, there is an ATA command to set up srbExtension->AtaFunction = ATA_FUNCTION_ATA_SMART; srbExtension->CompletionRoutine = AhciPortSmartCompletion; AhciZeroMemory((PCHAR) &srbExtension->TaskFile, sizeof(ATA_TASK_FILE)); srbExtension->TaskFile.Current.bFeaturesReg = inParams->irDriveRegs.bFeaturesReg; srbExtension->TaskFile.Current.bSectorCountReg = inParams->irDriveRegs.bSectorCountReg; srbExtension->TaskFile.Current.bSectorNumberReg = inParams->irDriveRegs.bSectorNumberReg; srbExtension->TaskFile.Current.bCylLowReg = inParams->irDriveRegs.bCylLowReg; srbExtension->TaskFile.Current.bCylHighReg = inParams->irDriveRegs.bCylHighReg; srbExtension->TaskFile.Current.bDriveHeadReg = 0xA0 | (inParams->irDriveRegs.bDriveHeadReg & 0x0F); srbExtension->TaskFile.Current.bCommandReg = inParams->irDriveRegs.bCommandReg; Srb->SrbStatus = SRB_STATUS_SUCCESS; return STOR_STATUS_SUCCESS; } ULONG NVCacheGeneric( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ PSTORAGE_REQUEST_BLOCK Srb ) { PSRB_IO_CONTROL srbControl; PNVCACHE_REQUEST_BLOCK nRB; PAHCI_SRB_EXTENSION srbExtension; ULONGLONG tempLBA; PNV_FEATURE_PARAMETER idCacheData; ULONG status; ULONG srbDataBufferLength = SrbGetDataTransferLength(Srb); ULONG srbFlags = SrbGetSrbFlags(Srb); PVOID resultBuffer = NULL; srbExtension = GetSrbExtension(Srb); srbControl = (PSRB_IO_CONTROL)SrbGetDataBuffer(Srb); nRB = ((PNVCACHE_REQUEST_BLOCK) ( (PSRB_IO_CONTROL)srbControl + 1) ); switch (nRB->Function) { //feature discovery case NRB_FUNCTION_NVCACHE_INFO: //Fill in the return _NV_FEATURE_PARAMETER and complete the command if ( srbDataBufferLength < (sizeof(SRB_IO_CONTROL) + sizeof(NVCACHE_REQUEST_BLOCK) + sizeof(NV_FEATURE_PARAMETER)) ) { if ( srbDataBufferLength >= (sizeof(SRB_IO_CONTROL) + RTL_SIZEOF_THROUGH_FIELD(NVCACHE_REQUEST_BLOCK, NRBStatus)) ) { nRB->NRBStatus = NRB_INVALID_PARAMETER; } Srb->SrbStatus = SRB_STATUS_BAD_SRB_BLOCK_LENGTH; return STOR_STATUS_BUFFER_TOO_SMALL; } idCacheData = (PNV_FEATURE_PARAMETER) ( ( (PNVCACHE_REQUEST_BLOCK)nRB + 1) ); idCacheData->NVPowerModeEnabled = ChannelExtension->DeviceExtension->IdentifyDeviceData->NVCacheCapabilities.NVCachePowerModeEnabled; idCacheData->NVCmdEnabled = ChannelExtension->DeviceExtension->IdentifyDeviceData->NVCacheCapabilities.NVCacheFeatureSetEnabled; idCacheData->NVPowerModeVer = ChannelExtension->DeviceExtension->IdentifyDeviceData->NVCacheCapabilities.NVCachePowerModeVersion; idCacheData->NVCmdVer = ChannelExtension->DeviceExtension->IdentifyDeviceData->NVCacheCapabilities.NVCacheFeatureSetVersion; idCacheData->NVSize = ChannelExtension->DeviceExtension->IdentifyDeviceData->NVCacheSizeMSW; idCacheData->NVSize <<= 16; idCacheData->NVSize += ChannelExtension->DeviceExtension->IdentifyDeviceData->NVCacheSizeLSW; idCacheData->NVReadSpeed = 0; // this field doesn't exist in ATA spec. idCacheData->NVWrtSpeed = 0; // this field doesn't exist in ATA spec. idCacheData->DeviceSpinUpTime = ChannelExtension->DeviceExtension->IdentifyDeviceData->NVCacheOptions.NVCacheEstimatedTimeToSpinUpInSeconds; Srb->SrbStatus = SRB_STATUS_SUCCESS; nRB->NRBStatus = NRB_SUCCESS; return STOR_STATUS_SUCCESS; break; //non data case NRB_FUNCTION_SPINDLE_STATUS: case NRB_FUNCTION_NVCACHE_POWER_MODE_SET: case NRB_FUNCTION_NVCACHE_POWER_MODE_RETURN: //Make sure this is not a data transfer. srbFlags &= ~(SRB_FLAGS_DATA_IN | SRB_FLAGS_DATA_OUT); SrbSetSrbFlags(Srb, srbFlags); srbExtension->Flags &= ~(SRB_FLAGS_DATA_IN | SRB_FLAGS_DATA_OUT); break; //data transfer case NRB_FUNCTION_QUERY_PINNED_SET: case NRB_FUNCTION_QUERY_CACHE_MISS: case NRB_FUNCTION_QUERY_HYBRID_DISK_STATUS: case NRB_FUNCTION_FLUSH_NVCACHE: case NRB_FUNCTION_ADD_LBAS_PINNED_SET: case NRB_FUNCTION_REMOVE_LBAS_PINNED_SET: case NRB_FUNCTION_QUERY_ASCENDER_STATUS: //Setup the outbuffer to hold the data transfered //Ensure the PRDT will get set up if( (nRB->Function == NRB_FUNCTION_QUERY_PINNED_SET) || (nRB->Function == NRB_FUNCTION_QUERY_CACHE_MISS) || (nRB->Function == NRB_FUNCTION_QUERY_HYBRID_DISK_STATUS) || (nRB->Function == NRB_FUNCTION_QUERY_ASCENDER_STATUS) ) { srbExtension->Flags |= ATA_FLAGS_DATA_IN; } else { srbExtension->Flags |= ATA_FLAGS_DATA_OUT; } if (! FillClippedSGL( StorPortGetScatterGatherList(ChannelExtension->AdapterExtension, (PSCSI_REQUEST_BLOCK)Srb), (PSTOR_SCATTER_GATHER_LIST) &srbExtension->LocalSgl, sizeof(SRB_IO_CONTROL) + sizeof(NVCACHE_REQUEST_BLOCK), srbDataBufferLength - sizeof(SRB_IO_CONTROL) - sizeof(NVCACHE_REQUEST_BLOCK) ) ) { if ( srbDataBufferLength >= (sizeof(SRB_IO_CONTROL) + RTL_SIZEOF_THROUGH_FIELD(NVCACHE_REQUEST_BLOCK, NRBStatus)) ) { nRB->NRBStatus = NRB_INVALID_PARAMETER; } Srb->SrbStatus = SRB_STATUS_BAD_SRB_BLOCK_LENGTH; return STOR_STATUS_BUFFER_TOO_SMALL; //there is no TOO_BIG status } else { srbExtension->Sgl = &srbExtension->LocalSgl; srbExtension->DataTransferLength = srbDataBufferLength - sizeof(SRB_IO_CONTROL) - sizeof(NVCACHE_REQUEST_BLOCK); } break; default: if ( srbDataBufferLength >= (sizeof(SRB_IO_CONTROL) + RTL_SIZEOF_THROUGH_FIELD(NVCACHE_REQUEST_BLOCK, NRBStatus)) ) { nRB->NRBStatus = NRB_ILLEGAL_REQUEST; } Srb->SrbStatus = SRB_STATUS_BAD_FUNCTION; return STOR_STATUS_INVALID_DEVICE_REQUEST; break; } // // Allocate and set SenseInfo buffer pointer - DMA friendly // status = AhciAllocateDmaBuffer(ChannelExtension->AdapterExtension, sizeof(ATA_TASK_FILE), &resultBuffer); if ( (status != STOR_STATUS_SUCCESS) || (resultBuffer == NULL) ) { // // Memory allocation failed // Srb->SrbStatus = SRB_STATUS_ERROR; return STOR_STATUS_INSUFFICIENT_RESOURCES; } AhciZeroMemory((PCHAR)resultBuffer, sizeof(ATA_TASK_FILE)); srbExtension->ResultBuffer = resultBuffer; srbExtension->ResultBufferLength = sizeof(ATA_TASK_FILE); // // Make sure to recieve the return task file // srbExtension->Flags |= ATA_FLAGS_RETURN_RESULTS; srbExtension->AtaFunction = ATA_FUNCTION_ATA_COMMAND; srbExtension->CompletionRoutine = AhciPortNVCacheCompletion; AhciZeroMemory((PCHAR) &srbExtension->TaskFile, sizeof(ATA_TASK_FILE)); srbExtension->TaskFile.Current.bFeaturesReg = (UCHAR) nRB->Function; srbExtension->TaskFile.Current.bSectorCountReg = (UCHAR) (0xFF & nRB->Count); srbExtension->TaskFile.Previous.bSectorCountReg =(UCHAR) (nRB->Count >> 8); tempLBA = nRB->LBA; srbExtension->TaskFile.Current.bSectorNumberReg = (UCHAR) (0xFF & tempLBA); tempLBA >>= 8; srbExtension->TaskFile.Current.bCylLowReg = (UCHAR) (0xFF & tempLBA); tempLBA >>= 8; srbExtension->TaskFile.Current.bCylHighReg = (UCHAR) (0xFF & tempLBA); tempLBA >>= 8; srbExtension->TaskFile.Previous.bSectorNumberReg = (UCHAR) (0xFF & tempLBA); tempLBA >>= 8; srbExtension->TaskFile.Previous.bCylLowReg = (UCHAR) (0xFF & tempLBA); tempLBA >>= 8; srbExtension->TaskFile.Previous.bCylHighReg = (UCHAR) (0xFF & tempLBA); srbExtension->TaskFile.Current.bDriveHeadReg = 0xA0; srbExtension->TaskFile.Current.bCommandReg = NVC_ATA_NV_CACHE_COMMAND; Srb->SrbStatus = SRB_STATUS_SUCCESS; return STOR_STATUS_SUCCESS; } VOID HybridInfoCompletion( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ PSTORAGE_REQUEST_BLOCK Srb ) { PSRB_IO_CONTROL srbControl; PHYBRID_REQUEST_BLOCK hybridRequest; PHYBRID_INFORMATION hybridInfo; PNVCACHE_PRIORITY_LEVEL_DESCRIPTOR priorityLevel; PGP_LOG_HYBRID_INFORMATION logPage; PAHCI_SRB_EXTENSION srbExtension; ULONG bufferLeftSize; int i; srbExtension = GetSrbExtension(Srb); srbControl = (PSRB_IO_CONTROL)SrbGetDataBuffer(Srb); hybridRequest = (PHYBRID_REQUEST_BLOCK)(srbControl + 1); hybridInfo = (PHYBRID_INFORMATION)((PUCHAR)srbControl + hybridRequest->DataBufferOffset); logPage = (PGP_LOG_HYBRID_INFORMATION)srbExtension->DataBuffer, NT_ASSERT(IsDeviceHybridInfoSupported(ChannelExtension)); if (Srb->SrbStatus != SRB_STATUS_SUCCESS) { AhciFreeDmaBuffer(ChannelExtension->AdapterExtension, ATA_BLOCK_SIZE, srbExtension->DataBuffer); StorPortDebugPrint(3, "StorAHCI - Hybrid: Port %02d - Hybrid Info log read failed. \n", ChannelExtension->PortNumber); return; } // // Cache Hybrid information. // StorPortCopyMemory(&ChannelExtension->DeviceExtension->HybridInfo, logPage, sizeof(GP_LOG_HYBRID_INFORMATION_HEADER)); // // Fill data into output buffer. // AhciZeroMemory((PCHAR)((PUCHAR)srbControl + hybridRequest->DataBufferOffset), hybridRequest->DataBufferLength); hybridInfo->Version = HYBRID_REQUEST_INFO_STRUCTURE_VERSION; hybridInfo->Size = sizeof(HYBRID_INFORMATION); hybridInfo->HybridSupported = IsDeviceHybridInfoSupported(ChannelExtension); if (logPage->Header.Enabled == HYBRID_INFORMATION_DISABLED) { hybridInfo->Status = NvCacheStatusDisabled; } else if (logPage->Header.Enabled == HYBRID_INFORMATION_DISABLE_IN_PROCESS) { hybridInfo->Status = NvCacheStatusDisabling; } else if (logPage->Header.Enabled == HYBRID_INFORMATION_ENABLED) { hybridInfo->Status = NvCacheStatusEnabled; } else { NT_ASSERTMSG("StorAHCI: Hardware issue - Enabled value from Hybrid Information log should be 0x00 or 0x80, or 0xFF.\n", FALSE); } hybridInfo->CacheTypeEffective = NvCacheTypeWriteBack; hybridInfo->CacheTypeDefault = NvCacheTypeWriteBack; hybridInfo->FractionBase = 0xFF; hybridInfo->CacheSize = logPage->Header.NVMSize; hybridInfo->Attributes.WriteCacheChangeable = ChannelExtension->DeviceExtension->SupportedCommands.HybridControl; hybridInfo->Attributes.WriteThroughIoSupported = ChannelExtension->DeviceExtension->SupportedCommands.HybridEvict; hybridInfo->Attributes.FlushCacheSupported = ChannelExtension->DeviceExtension->SupportedCommands.HybridControl; hybridInfo->Attributes.Removable = FALSE; hybridInfo->Priorities.PriorityLevelCount = logPage->Header.MaximumHybridPriorityLevel + 1; hybridInfo->Priorities.MaxPriorityBehavior = logPage->Header.SupportedOptions.MaximumPriorityBehavior; hybridInfo->Priorities.OptimalWriteGranularity = logPage->Header.OptimalWriteGranularity; hybridInfo->Priorities.DirtyThresholdLow = logPage->Header.DirtyLowThreshold; hybridInfo->Priorities.DirtyThresholdHigh = logPage->Header.DirtyHighThreshold; hybridInfo->Priorities.SupportedCommands.CacheDisable = ChannelExtension->DeviceExtension->SupportedCommands.HybridControl; hybridInfo->Priorities.SupportedCommands.SetDirtyThreshold = ChannelExtension->DeviceExtension->SupportedCommands.HybridControl; hybridInfo->Priorities.SupportedCommands.PriorityDemoteBySize = ChannelExtension->DeviceExtension->SupportedCommands.HybridDemoteBySize; hybridInfo->Priorities.SupportedCommands.PriorityChangeByLbaRange = ChannelExtension->DeviceExtension->SupportedCommands.HybridChangeByLbaRange; hybridInfo->Priorities.SupportedCommands.Evict = ChannelExtension->DeviceExtension->SupportedCommands.HybridEvict; hybridInfo->Priorities.SupportedCommands.MaxEvictCommands = logPage->Header.MaximumEvictionCommands; hybridInfo->Priorities.SupportedCommands.MaxLbaRangeCountForEvict = (ATA_BLOCK_SIZE / sizeof(ATA_LBA_RANGE)) * logPage->Header.MaximumEvictionDataBlocks; hybridInfo->Priorities.SupportedCommands.MaxLbaRangeCountForChangeLba = GetHybridMaxLbaRangeCountForChangeLba(ChannelExtension); priorityLevel = hybridInfo->Priorities.Priority; bufferLeftSize = hybridRequest->DataBufferLength - sizeof(HYBRID_INFORMATION); for (i = 0; i < logPage->Header.HybridInfoDescrCount; i++) { if (bufferLeftSize >= sizeof(NVCACHE_PRIORITY_LEVEL_DESCRIPTOR)) { priorityLevel->PriorityLevel = logPage->Descriptor[i].HybridPriority; priorityLevel->ConsumedNVMSizeFraction = logPage->Descriptor[i].ConsumedNVMSizeFraction; priorityLevel->ConsumedMappingResourcesFraction = logPage->Descriptor[i].ConsumedMappingResourcesFraction; priorityLevel->ConsumedNVMSizeForDirtyDataFraction = logPage->Descriptor[i].ConsumedNVMSizeForDirtyDataFraction; priorityLevel->ConsumedMappingResourcesForDirtyDataFraction = logPage->Descriptor[i].ConsumedMappingResourcesForDirtyDataFraction; // // Update pointer and left buffer size // priorityLevel = priorityLevel + 1; bufferLeftSize -= sizeof(NVCACHE_PRIORITY_LEVEL_DESCRIPTOR); } else { break; } } if (i < logPage->Header.HybridInfoDescrCount) { srbControl->ReturnCode = HYBRID_STATUS_OUTPUT_BUFFER_TOO_SMALL; hybridRequest->DataBufferLength = sizeof(HYBRID_INFORMATION) + sizeof(NVCACHE_PRIORITY_LEVEL_DESCRIPTOR) * logPage->Header.HybridInfoDescrCount; Srb->SrbStatus = SRB_STATUS_DATA_OVERRUN; } SrbSetDataTransferLength(Srb, hybridRequest->DataBufferOffset + hybridRequest->DataBufferLength); AhciFreeDmaBuffer(ChannelExtension->AdapterExtension, ATA_BLOCK_SIZE, srbExtension->DataBuffer); StorPortDebugPrint(3, "StorAHCI - Hybrid: Port %02d - Hybrid Info log read successfully. \n", ChannelExtension->PortNumber); return; } ULONG HybridGetInfo( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ PSTORAGE_REQUEST_BLOCK Srb ) /*++ Routine Description: IOCTL handling routine processes HYBRID request - Get Info. Arguments: ChannelExtension SRB Return Value: STOR Status --*/ { ULONG status; PSRB_IO_CONTROL srbControl; PHYBRID_REQUEST_BLOCK hybridRequest; srbControl = (PSRB_IO_CONTROL)SrbGetDataBuffer(Srb); hybridRequest = (PHYBRID_REQUEST_BLOCK)(srbControl + 1); // // Validate buffer length // if (hybridRequest->DataBufferOffset < (sizeof(SRB_IO_CONTROL) + sizeof(HYBRID_REQUEST_BLOCK))) { // // Output buffer should be after these two data structures. // srbControl->ReturnCode = HYBRID_STATUS_INVALID_PARAMETER; Srb->SrbStatus = SRB_STATUS_BAD_SRB_BLOCK_LENGTH; return STOR_STATUS_INVALID_PARAMETER; } if ((SrbGetDataTransferLength(Srb) < (sizeof(SRB_IO_CONTROL) + sizeof(HYBRID_REQUEST_BLOCK) + sizeof(HYBRID_INFORMATION))) || (hybridRequest->DataBufferLength < sizeof(HYBRID_INFORMATION))) { // // Output buffer after HYBRID_REQUEST_BLOCK should be at least with length of sizeof(HYBRID_INFORMATION). // srbControl->ReturnCode = HYBRID_STATUS_OUTPUT_BUFFER_TOO_SMALL; hybridRequest->DataBufferLength = sizeof(HYBRID_INFORMATION); Srb->SrbStatus = SRB_STATUS_DATA_OVERRUN; return STOR_STATUS_BUFFER_TOO_SMALL; } if (!IsDeviceHybridInfoSupported(ChannelExtension)) { // // If Hybrid Information is not supported, complete the request with "HybridSupported" field set to "FALSE". // PHYBRID_INFORMATION hybridInfo; hybridInfo = (PHYBRID_INFORMATION)((PUCHAR)srbControl + hybridRequest->DataBufferOffset); hybridInfo->Version = HYBRID_REQUEST_INFO_STRUCTURE_VERSION; hybridInfo->Size = sizeof(HYBRID_INFORMATION); hybridInfo->HybridSupported = FALSE; Srb->SrbStatus = SRB_STATUS_SUCCESS; status = STOR_STATUS_SUCCESS; } else { PVOID logPageBuffer = NULL; STOR_PHYSICAL_ADDRESS logPagePhysialAddress; ULONG tempLength; // // We need to allocate a new data buffer for log page // status = AhciAllocateDmaBuffer(ChannelExtension->AdapterExtension, ATA_BLOCK_SIZE, &logPageBuffer); if ( (status != STOR_STATUS_SUCCESS) || (logPageBuffer == NULL) ) { // memory allocation failed Srb->SrbStatus = SRB_STATUS_ERROR; return STOR_STATUS_INSUFFICIENT_RESOURCES; } AhciZeroMemory((PCHAR)logPageBuffer, ATA_BLOCK_SIZE); logPagePhysialAddress = StorPortGetPhysicalAddress(ChannelExtension->AdapterExtension, NULL, (PVOID)logPageBuffer, &tempLength); // // Note that logPageBuffer will be released in completion routine. // IssueReadLogExtCommand( ChannelExtension, Srb, IDE_GP_LOG_HYBRID_INFO_ADDRESS, 0, 1, 0, // feature field &logPagePhysialAddress, logPageBuffer, HybridInfoCompletion ); } return status; } VOID HybridControlCachingMediumCompletion( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ PSTORAGE_REQUEST_BLOCK Srb ) { PAHCI_SRB_EXTENSION srbExtension; // // Command failed, bail out. // if (Srb->SrbStatus != SRB_STATUS_SUCCESS) { return; } srbExtension = GetSrbExtension(Srb); // // This completion routine is only used by HybridDisableCachingMedium() and HybridEnableCachingMedium(). // // // Update cached data based on command completion status. This is a work around for not sending an IDENTIFY DEVICE command. // if ((srbExtension->Cfis.Command == IDE_COMMAND_SET_FEATURE) && (srbExtension->Cfis.Feature7_0 == IDE_FEATURE_ENABLE_SATA_FEATURE) && (srbExtension->Cfis.SectorCount == IDE_SATA_FEATURE_HYBRID_INFORMATION)) { ChannelExtension->DeviceExtension[0].IdentifyDeviceData->SerialAtaFeaturesEnabled.HybridInformation = 1; StorPortDebugPrint(3, "StorAHCI - Hybrid: Port %02d - Caching Medium Enabled. \n", ChannelExtension->PortNumber); } else { // // Caching medium might be in Disabling state at this time. // Updating following value now to refresh storahci validation for future hybrid request. // ChannelExtension->DeviceExtension[0].IdentifyDeviceData->SerialAtaFeaturesEnabled.HybridInformation = 0; StorPortDebugPrint(3, "StorAHCI - Hybrid: Port %02d - Caching Medium is being Disabled. \n", ChannelExtension->PortNumber); } return; } __inline VOID BuildHybridControlDisableCacheCommand( _Inout_ PAHCI_H2D_REGISTER_FIS CFIS ) /*++ Routine Description: Build Hybrid Control command to Disable Caching Medium. Arguments: CFIS - the buffer should be zero-ed before calling this function. Return Value: None --*/ { CFIS->Feature7_0 = IDE_NCQ_NON_DATA_HYBRID_CONTROL; CFIS->Feature7_0 |= 0x80; // Disable Caching Medium bit. CFIS->Device |= (1 << 6); CFIS->Command = IDE_COMMAND_NCQ_NON_DATA; } ULONG HybridDisableCachingMedium( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ PSTORAGE_REQUEST_BLOCK Srb ) /*++ Routine Description: IOCTL handling routine processes HYBRID request - Disable Caching Medium. Arguments: ChannelExtension SRB Return Value: STOR Status --*/ { PSRB_IO_CONTROL srbControl; PAHCI_SRB_EXTENSION srbExtension; LONG hybridCachingMediumEnableRefs; srbControl = (PSRB_IO_CONTROL)SrbGetDataBuffer(Srb); srbExtension = GetSrbExtension(Srb); // // Fail the request if NCQ is not supported or hybrid information feature is not supported. // if (!IsNCQSupported(ChannelExtension) || (ChannelExtension->StateFlags.NCQ_Activated == 0) || !IsDeviceHybridInfoSupported(ChannelExtension) ) { srbControl->ReturnCode = HYBRID_STATUS_ILLEGAL_REQUEST; Srb->SrbStatus = SRB_STATUS_BAD_FUNCTION; return STOR_STATUS_INVALID_PARAMETER; } // // Fail the request if command is not supported. // if (ChannelExtension->DeviceExtension->SupportedCommands.HybridControl == 0) { srbControl->ReturnCode = HYBRID_STATUS_ILLEGAL_REQUEST; Srb->SrbStatus = SRB_STATUS_BAD_FUNCTION; return STOR_STATUS_INVALID_PARAMETER; } // // If Hybrid feature is disabled, complete the call as success. // if (!IsDeviceHybridInfoEnabled(ChannelExtension)) { NT_ASSERT(ChannelExtension->DeviceExtension->HybridCachingMediumEnableRefs == 0); Srb->SrbStatus = SRB_STATUS_SUCCESS; StorPortDebugPrint(3, "StorAHCI - Hybrid: Port %02d - Caching Medium is already disabled. No command sent to disk. \n", ChannelExtension->PortNumber); return STOR_STATUS_SUCCESS; } // // If Hybrid feature is enabled and refcount is more than 1, complete the call. // hybridCachingMediumEnableRefs = InterlockedDecrement(&ChannelExtension->DeviceExtension->HybridCachingMediumEnableRefs); if (hybridCachingMediumEnableRefs > 0) { srbControl->ReturnCode = HYBRID_STATUS_ENABLE_REFCOUNT_HOLD; Srb->SrbStatus = SRB_STATUS_ABORTED; return STOR_STATUS_UNSUCCESSFUL; } // // Add count back if it was 0 (or negative value) before decrement. // if (hybridCachingMediumEnableRefs < 0) { InterlockedIncrement(&ChannelExtension->DeviceExtension->HybridCachingMediumEnableRefs); } // // Build command into CFIS data structure. // BuildHybridControlDisableCacheCommand(&srbExtension->Cfis); srbExtension->AtaFunction = ATA_FUNCTION_ATA_CFIS_PAYLOAD; // // Note that if a synchronized behavior is wanted, the caller should check Hybrid Information log (or identify device data), // until the caching medium is in disabled state (or hybrid information feature is disabled). // srbExtension->CompletionRoutine = HybridControlCachingMediumCompletion; return STOR_STATUS_SUCCESS; } __inline VOID BuildEnableFeatureHybridInfoCommand( _Inout_ PAHCI_H2D_REGISTER_FIS CFIS ) /*++ Routine Description: Build Set Feature command to enable Hybrid Information feature. Arguments: CFIS - the buffer should be zero-ed before calling this function. Return Value: None --*/ { CFIS->Feature7_0 = IDE_FEATURE_ENABLE_SATA_FEATURE; CFIS->SectorCount = IDE_SATA_FEATURE_HYBRID_INFORMATION; CFIS->Command = IDE_COMMAND_SET_FEATURE; } ULONG HybridEnableCachingMedium( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ PSTORAGE_REQUEST_BLOCK Srb ) /*++ Routine Description: IOCTL handling routine processes HYBRID request - Enable Caching Medium. Arguments: ChannelExtension SRB Return Value: STOR Status --*/ { PSRB_IO_CONTROL srbControl; PAHCI_SRB_EXTENSION srbExtension; srbControl = (PSRB_IO_CONTROL)SrbGetDataBuffer(Srb); srbExtension = GetSrbExtension(Srb); // // Fail the request if NCQ is not supported, or hybrid information feature is not supported. // if (!IsNCQSupported(ChannelExtension) || (ChannelExtension->StateFlags.NCQ_Activated == 0) || !IsDeviceHybridInfoSupported(ChannelExtension)) { srbControl->ReturnCode = HYBRID_STATUS_ILLEGAL_REQUEST; Srb->SrbStatus = SRB_STATUS_BAD_FUNCTION; return STOR_STATUS_INVALID_PARAMETER; } // // Fail the request if command is not supported. // if (ChannelExtension->DeviceExtension->SupportedCommands.HybridControl == 0) { srbControl->ReturnCode = HYBRID_STATUS_ILLEGAL_REQUEST; Srb->SrbStatus = SRB_STATUS_BAD_FUNCTION; return STOR_STATUS_INVALID_PARAMETER; } InterlockedIncrement(&ChannelExtension->DeviceExtension->HybridCachingMediumEnableRefs); // // If Hybrid feature is enabled, complete the call as success. // if (IsDeviceHybridInfoEnabled(ChannelExtension)) { Srb->SrbStatus = SRB_STATUS_SUCCESS; StorPortDebugPrint(3, "StorAHCI - Hybrid: Port %02d - Caching Medium is already enabled. No command sent to disk. \n", ChannelExtension->PortNumber); return STOR_STATUS_SUCCESS; } // // Build command into CFIS data structure. // BuildEnableFeatureHybridInfoCommand(&srbExtension->Cfis); srbExtension->AtaFunction = ATA_FUNCTION_ATA_CFIS_PAYLOAD; srbExtension->CompletionRoutine = HybridControlCachingMediumCompletion; return STOR_STATUS_SUCCESS; } __inline VOID BuildHybridControlSetThresholdCommand( _Inout_ PAHCI_H2D_REGISTER_FIS CFIS, _In_ UCHAR DirtyLowThreshold, _In_ UCHAR DirtyHighThreshold ) /*++ Routine Description: Build Hybrid Control command to set Dirty Low and High Threshold. Arguments: CFIS - the buffer should be zero-ed before calling this function. Return Value: None --*/ { CFIS->Feature7_0 = IDE_NCQ_NON_DATA_HYBRID_CONTROL; CFIS->LBA7_0 = DirtyLowThreshold; CFIS->LBA15_8 = DirtyHighThreshold; CFIS->Device |= (1 << 6); CFIS->Command = IDE_COMMAND_NCQ_NON_DATA; } ULONG HybridSetDirtyThreshold( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ PSTORAGE_REQUEST_BLOCK Srb ) /*++ Routine Description: IOCTL handling routine processes HYBRID request - Set Dirty Threshold. Arguments: ChannelExtension SRB Return Value: STOR Status --*/ { PSRB_IO_CONTROL srbControl; PHYBRID_REQUEST_BLOCK hybridRequest; PHYBRID_DIRTY_THRESHOLDS hybridDirtyThresholds; PAHCI_SRB_EXTENSION srbExtension; srbControl = (PSRB_IO_CONTROL)SrbGetDataBuffer(Srb); hybridRequest = (PHYBRID_REQUEST_BLOCK)(srbControl + 1); srbExtension = GetSrbExtension(Srb); if (hybridRequest->DataBufferOffset < (sizeof(SRB_IO_CONTROL) + sizeof(HYBRID_REQUEST_BLOCK))) { // // HYBRID_DIRTY_THRESHOLDS should be after these two data structures. // srbControl->ReturnCode = HYBRID_STATUS_INVALID_PARAMETER; Srb->SrbStatus = SRB_STATUS_BAD_SRB_BLOCK_LENGTH; return STOR_STATUS_INVALID_PARAMETER; } if ((SrbGetDataTransferLength(Srb) < (sizeof(SRB_IO_CONTROL) + sizeof(HYBRID_REQUEST_BLOCK) + sizeof(HYBRID_DIRTY_THRESHOLDS))) || (hybridRequest->DataBufferLength < sizeof(HYBRID_DIRTY_THRESHOLDS))) { // // Input buffer after HYBRID_REQUEST_BLOCK should be at least with length of sizeof(HYBRID_DIRTY_THRESHOLDS). // srbControl->ReturnCode = HYBRID_STATUS_OUTPUT_BUFFER_TOO_SMALL; hybridRequest->DataBufferLength = sizeof(HYBRID_DIRTY_THRESHOLDS); Srb->SrbStatus = SRB_STATUS_DATA_OVERRUN; return STOR_STATUS_BUFFER_TOO_SMALL; } hybridDirtyThresholds = (PHYBRID_DIRTY_THRESHOLDS)((PUCHAR)srbControl + hybridRequest->DataBufferOffset); if ((hybridDirtyThresholds->DirtyLowThreshold > 255) || (hybridDirtyThresholds->DirtyHighThreshold > 255) || (hybridDirtyThresholds->DirtyLowThreshold > hybridDirtyThresholds->DirtyHighThreshold)) { srbControl->ReturnCode = HYBRID_STATUS_INVALID_PARAMETER; Srb->SrbStatus = SRB_STATUS_BAD_SRB_BLOCK_LENGTH; return STOR_STATUS_INVALID_PARAMETER; } // // Fail the request if NCQ is not supported. // if (!IsNCQSupported(ChannelExtension) || (ChannelExtension->StateFlags.NCQ_Activated == 0)) { srbControl->ReturnCode = HYBRID_STATUS_ILLEGAL_REQUEST; Srb->SrbStatus = SRB_STATUS_BAD_FUNCTION; return STOR_STATUS_INVALID_PARAMETER; } // // If device doesn't support Hybrid or the feature is disabled, fail the request. // if (!IsDeviceHybridInfoSupported(ChannelExtension) || !IsDeviceHybridInfoEnabled(ChannelExtension) || (ChannelExtension->DeviceExtension->SupportedCommands.HybridControl == 0)) { srbControl->ReturnCode = HYBRID_STATUS_ILLEGAL_REQUEST; Srb->SrbStatus = SRB_STATUS_BAD_FUNCTION; return STOR_STATUS_INVALID_PARAMETER; } // // Build command into CFIS data structure. // BuildHybridControlSetThresholdCommand(&srbExtension->Cfis, (UCHAR)hybridDirtyThresholds->DirtyLowThreshold, (UCHAR)hybridDirtyThresholds->DirtyHighThreshold); srbExtension->AtaFunction = ATA_FUNCTION_ATA_CFIS_PAYLOAD; StorPortDebugPrint(3, "StorAHCI - Hybrid: Port %02d - Set Dirty Threshold, command sent to device. Low: 0x%02X, High: 0x%02X. \n", ChannelExtension->PortNumber, hybridDirtyThresholds->DirtyLowThreshold, hybridDirtyThresholds->DirtyHighThreshold); return STOR_STATUS_SUCCESS; } __inline VOID BuildHybridDemoteBySizeCommand( _Inout_ PAHCI_H2D_REGISTER_FIS CFIS, _In_ UCHAR SourcePriority, _In_ UCHAR TargetPriority, _In_ ULONG LbaCount ) /*++ Routine Description: Build Hybrid Control command to set Dirty Low and High Threshold. Arguments: CFIS - the buffer should be zero-ed before calling this function. Return Value: None --*/ { ATA_HYBRID_INFO_FIELDS hybridInfo = {0}; hybridInfo.InfoValid = 1; hybridInfo.HybridPriority = TargetPriority; CFIS->Feature7_0 = IDE_NCQ_NON_DATA_HYBRID_DEMOTE_BY_SIZE; CFIS->Feature7_0 |= (UCHAR)(SourcePriority << 4); CFIS->Feature15_8 = (UCHAR)LbaCount; CFIS->Count15_8 = (UCHAR)(LbaCount >> 8); CFIS->LBA7_0 = (UCHAR)(LbaCount >> 16); CFIS->LBA15_8 = (UCHAR)(LbaCount >> 24); CFIS->Auxiliary23_16 = hybridInfo.AsUchar; CFIS->Device |= (1 << 6); CFIS->Command = IDE_COMMAND_NCQ_NON_DATA; } ULONG HybridDemoteBySize( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ PSTORAGE_REQUEST_BLOCK Srb ) /*++ Routine Description: IOCTL handling routine processes HYBRID request - Demote By Size. Arguments: ChannelExtension SRB Return Value: STOR Status --*/ { PSRB_IO_CONTROL srbControl; PHYBRID_REQUEST_BLOCK hybridRequest; PHYBRID_DEMOTE_BY_SIZE hybridDemoteBySize; PAHCI_SRB_EXTENSION srbExtension; srbControl = (PSRB_IO_CONTROL)SrbGetDataBuffer(Srb); hybridRequest = (PHYBRID_REQUEST_BLOCK)(srbControl + 1); srbExtension = GetSrbExtension(Srb); if (hybridRequest->DataBufferOffset < (sizeof(SRB_IO_CONTROL) + sizeof(HYBRID_REQUEST_BLOCK))) { // // HYBRID_DEMOTE_BY_SIZE should be after these two data structures. // srbControl->ReturnCode = HYBRID_STATUS_INVALID_PARAMETER; Srb->SrbStatus = SRB_STATUS_BAD_SRB_BLOCK_LENGTH; return STOR_STATUS_INVALID_PARAMETER; } if ((SrbGetDataTransferLength(Srb) < (sizeof(SRB_IO_CONTROL) + sizeof(HYBRID_REQUEST_BLOCK) + sizeof(HYBRID_DEMOTE_BY_SIZE))) || (hybridRequest->DataBufferLength < sizeof(HYBRID_DEMOTE_BY_SIZE))) { // // Input buffer after HYBRID_REQUEST_BLOCK should be at least with length of sizeof(HYBRID_DEMOTE_BY_SIZE). // srbControl->ReturnCode = HYBRID_STATUS_OUTPUT_BUFFER_TOO_SMALL; hybridRequest->DataBufferLength = sizeof(HYBRID_DEMOTE_BY_SIZE); Srb->SrbStatus = SRB_STATUS_DATA_OVERRUN; return STOR_STATUS_BUFFER_TOO_SMALL; } hybridDemoteBySize = (PHYBRID_DEMOTE_BY_SIZE)((PUCHAR)srbControl + hybridRequest->DataBufferOffset); if ((hybridDemoteBySize->SourcePriority > ChannelExtension->DeviceExtension->HybridInfo.MaximumHybridPriorityLevel) || (hybridDemoteBySize->TargetPriority > ChannelExtension->DeviceExtension->HybridInfo.MaximumHybridPriorityLevel) || (hybridDemoteBySize->TargetPriority >= hybridDemoteBySize->SourcePriority) || (hybridDemoteBySize->LbaCount == 0)) { srbControl->ReturnCode = HYBRID_STATUS_INVALID_PARAMETER; Srb->SrbStatus = SRB_STATUS_BAD_SRB_BLOCK_LENGTH; return STOR_STATUS_INVALID_PARAMETER; } // // Fail the request if NCQ is not supported. // if (!IsNCQSupported(ChannelExtension) || (ChannelExtension->StateFlags.NCQ_Activated == 0)) { srbControl->ReturnCode = HYBRID_STATUS_ILLEGAL_REQUEST; Srb->SrbStatus = SRB_STATUS_BAD_FUNCTION; return STOR_STATUS_INVALID_PARAMETER; } // // If device doesn't support Hybrid or the feature is disabled, fail the request. // if (!IsDeviceHybridInfoSupported(ChannelExtension) || !IsDeviceHybridInfoEnabled(ChannelExtension) || (ChannelExtension->DeviceExtension->SupportedCommands.HybridDemoteBySize == 0)) { srbControl->ReturnCode = HYBRID_STATUS_ILLEGAL_REQUEST; Srb->SrbStatus = SRB_STATUS_BAD_FUNCTION; return STOR_STATUS_INVALID_PARAMETER; } // // Build command into CFIS data structure. // BuildHybridDemoteBySizeCommand(&srbExtension->Cfis, (UCHAR)hybridDemoteBySize->SourcePriority, (UCHAR)hybridDemoteBySize->TargetPriority, (ULONG)hybridDemoteBySize->LbaCount); srbExtension->AtaFunction = ATA_FUNCTION_ATA_CFIS_PAYLOAD; StorPortDebugPrint(3, "StorAHCI - Hybrid: Port %02d - Demote by Size, command sent to device. Source Priority: %d, Target Priority: %d, LBA Count: %d. \n", ChannelExtension->PortNumber, (UCHAR)hybridDemoteBySize->SourcePriority, (UCHAR)hybridDemoteBySize->TargetPriority, (ULONG)hybridDemoteBySize->LbaCount); return STOR_STATUS_SUCCESS; } ULONG HybridIoctlProcess( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ PSTORAGE_REQUEST_BLOCK Srb ) /*++ Routine Description: IOCTL dispatch routine processes HYBRID request from storport Arguments: ChannelExtension SRB Return Value: STOR Status --*/ { ULONG status; ULONGLONG srbDataBufferLength; PSRB_IO_CONTROL srbControl; PHYBRID_REQUEST_BLOCK hybridRequest; srbControl = (PSRB_IO_CONTROL)SrbGetDataBuffer(Srb); srbDataBufferLength = SrbGetDataTransferLength(Srb); // // A Hybrid request must have at least SRB_IO_CONTROL and HYBRID_REQUEST_BLOCK in input buffer. // if (srbDataBufferLength < ((ULONGLONG)sizeof(SRB_IO_CONTROL) + sizeof(HYBRID_REQUEST_BLOCK))) { Srb->SrbStatus = SRB_STATUS_BAD_SRB_BLOCK_LENGTH; return STOR_STATUS_INVALID_PARAMETER; } hybridRequest = (PHYBRID_REQUEST_BLOCK)(srbControl + 1); if (srbDataBufferLength < ((ULONGLONG)hybridRequest->DataBufferOffset + hybridRequest->DataBufferLength)) { srbControl->ReturnCode = HYBRID_STATUS_INVALID_PARAMETER; Srb->SrbStatus = SRB_STATUS_BAD_SRB_BLOCK_LENGTH; return STOR_STATUS_INVALID_PARAMETER; } // process NVCACHE request according to function switch (hybridRequest->Function) { case HYBRID_FUNCTION_GET_INFO: status = HybridGetInfo (ChannelExtension, Srb); break; case HYBRID_FUNCTION_DISABLE_CACHING_MEDIUM: status = HybridDisableCachingMedium (ChannelExtension, Srb); break; case HYBRID_FUNCTION_ENABLE_CACHING_MEDIUM: status = HybridEnableCachingMedium (ChannelExtension, Srb); break; case HYBRID_FUNCTION_SET_DIRTY_THRESHOLD: status = HybridSetDirtyThreshold (ChannelExtension, Srb); break; case HYBRID_FUNCTION_DEMOTE_BY_SIZE: status = HybridDemoteBySize (ChannelExtension, Srb); break; default: Srb->SrbStatus = SRB_STATUS_BAD_SRB_BLOCK_LENGTH; status = STOR_STATUS_INVALID_PARAMETER; break; } return status; } ULONG ConvertDataSetRangeToAtaLbaRanges( _Inout_ PULONGLONG CurrentRangeStartLba, _Inout_ PULONGLONG CurrentRangeLbaCount, _In_reads_bytes_(BufferSize) PCHAR DestBuffer, _In_ ULONG BufferSize ) /*++ Routine Description: Convert current DataSet Range entry to be ATA_LBA_RANGE entries. CurrentRangeLbaCount is 64 bits; ATA_LBA_RANGE->SectorCount is 16 bits. It's possible that current DataSet Range entry needs multiple ATA_LBA_RANGE entries. Arguments: CurrentRangeStartLba CurrentRangeLbaCount DestBuffer BufferSize Return Value: Count of ATA_LBA_RANGE entries converted. NOTE: if CurrentRangeLbaCount does not reach to 0, the conversion for DEVICE_DATA_SET_RANGE entry is not completed. Further conversion is needed by calling this function again. --*/ { ULONG convertedEntryCount = 0; PATA_LBA_RANGE lbaRangeEntry = (PATA_LBA_RANGE)DestBuffer; ULONGLONG dataSetRangeStartLba = *CurrentRangeStartLba; ULONGLONG dataSetRangeLbaCount = *CurrentRangeLbaCount; // // fill in ATA_LBA_RANGE entries as needed // while ((dataSetRangeLbaCount > 0) && (convertedEntryCount * sizeof(ATA_LBA_RANGE) < BufferSize)) { USHORT sectorCount; if (dataSetRangeLbaCount > MAX_ATA_LBA_RANGE_SECTOR_COUNT_VALUE) { sectorCount = MAX_ATA_LBA_RANGE_SECTOR_COUNT_VALUE; } else { sectorCount = (USHORT)dataSetRangeLbaCount; } lbaRangeEntry[convertedEntryCount].StartSector = dataSetRangeStartLba; lbaRangeEntry[convertedEntryCount].SectorCount = sectorCount; dataSetRangeStartLba += sectorCount; dataSetRangeLbaCount -= sectorCount; convertedEntryCount++; } // // update current DataSet Range entry // *CurrentRangeStartLba = dataSetRangeStartLba; *CurrentRangeLbaCount = dataSetRangeLbaCount; return convertedEntryCount; } __inline VOID BuildHybridChangeByLbaCommand( _Inout_ PAHCI_H2D_REGISTER_FIS CFIS, _In_ UCHAR TargetPriority, _In_ ULONGLONG StartLba, _In_ USHORT LbaCount, _In_ BOOLEAN CacheBehavior ) /*++ Routine Description: Build Hybrid Control command to set Dirty Low and High Threshold. Arguments: CFIS - the buffer should be zero-ed before calling this function. Return Value: None --*/ { ATA_HYBRID_INFO_FIELDS hybridInfo = {0}; hybridInfo.InfoValid = 1; hybridInfo.HybridPriority = TargetPriority; CFIS->Feature7_0 = IDE_NCQ_NON_DATA_HYBRID_CHANGE_BY_LBA_RANGE; // // Set value of CB - CacheBehavior bit (bit4) into Features(7:0) // if (CacheBehavior == TRUE) { CFIS->Feature7_0 |= (1 << 4); } CFIS->Feature15_8 = (UCHAR)LbaCount; CFIS->Count15_8 = (UCHAR)(LbaCount >> 8); CFIS->LBA7_0 = (UCHAR)(StartLba); CFIS->LBA15_8 = (UCHAR)(StartLba >> 8); CFIS->LBA23_16 = (UCHAR)(StartLba >> 16); CFIS->LBA31_24 = (UCHAR)(StartLba >> 24); CFIS->LBA39_32 = (UCHAR)(StartLba >> 32); CFIS->LBA47_40 = (UCHAR)(StartLba >> 40); CFIS->Auxiliary23_16 = hybridInfo.AsUchar; CFIS->Device |= (1 << 6); CFIS->Command = IDE_COMMAND_NCQ_NON_DATA; } VOID HybridChangeByLbaCompletion( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ PSTORAGE_REQUEST_BLOCK Srb ) { PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(Srb); PHYBRID_CHANGE_BY_LBA_CONTEXT lbaRangeContext = (PHYBRID_CHANGE_BY_LBA_CONTEXT)srbExtension->CompletionContext; BOOLEAN completed = FALSE; ATA_LBA_RANGE ataLbaRange = {0}; BOOLEAN tempDataSetRangeConverted = TRUE; ULONG convertedEntryCount = 0; ULONG logicalSectorSize = BytesPerLogicalSector(&ChannelExtension->DeviceExtension->DeviceParameters); completed = ((Srb->SrbStatus != SRB_STATUS_PENDING) && (Srb->SrbStatus != SRB_STATUS_SUCCESS)) || (lbaRangeContext->ProcessedLbaRangeEntryCount >= lbaRangeContext->NeededLbaRangeEntryCount); if (!completed && (logicalSectorSize > 0)) { // // when first time this function is called, lbaRangeContext->CurrentDataSetRange.LengthInBytes should be '0' ( initialized to 0) // so that the first DataSet Range will be copied to lbaRangeContext->CurrentDataSetRange // tempDataSetRangeConverted = (lbaRangeContext->CurrentRangeLbaCount == 0)? TRUE : FALSE; // // if the previous entry conversion completed, continue the next one; // otherwise, still process the left part of the un-completed entry. // if (tempDataSetRangeConverted) { lbaRangeContext->CurrentRangeStartLba = lbaRangeContext->DataSetRanges[lbaRangeContext->DataSetRangeIndex].StartingOffset / logicalSectorSize; lbaRangeContext->CurrentRangeLbaCount = lbaRangeContext->DataSetRanges[lbaRangeContext->DataSetRangeIndex].LengthInBytes / logicalSectorSize; lbaRangeContext->DataSetRangeIndex++; } // // HybridChangeByLBA command can only carry one LBA range. So just convert one. // convertedEntryCount = ConvertDataSetRangeToAtaLbaRanges(&lbaRangeContext->CurrentRangeStartLba, &lbaRangeContext->CurrentRangeLbaCount, (PCHAR)&ataLbaRange, sizeof(ATA_LBA_RANGE) ); NT_ASSERT(convertedEntryCount == 1); lbaRangeContext->ProcessedLbaRangeEntryCount += convertedEntryCount; // // Send HybridChangeByLBA command with converted LBA range. // AhciZeroMemory((PCHAR)&srbExtension->Cfis, sizeof(AHCI_H2D_REGISTER_FIS)); BuildHybridChangeByLbaCommand(&srbExtension->Cfis, (UCHAR)lbaRangeContext->TargetPriority, ataLbaRange.StartSector, (USHORT)ataLbaRange.SectorCount, (ChannelExtension->DeviceExtension->HybridInfo.SupportedOptions.SupportCacheBehavior == 1)); srbExtension->CompletionRoutine = HybridChangeByLbaCompletion; srbExtension->CompletionContext = lbaRangeContext; srbExtension->AtaFunction = ATA_FUNCTION_ATA_CFIS_PAYLOAD; return; } else { if (!completed && (logicalSectorSize == 0)) { Srb->SrbStatus = SRB_STATUS_INVALID_LUN; } if (lbaRangeContext != NULL) { StorPortFreePool((PVOID)ChannelExtension->AdapterExtension, lbaRangeContext); } srbExtension->CompletionContext = NULL; srbExtension->CompletionRoutine = NULL; srbExtension->AtaFunction = 0; StorPortDebugPrint(3, "StorAHCI - Hybrid: Port %02d - Change by LBA command %s. \n", ChannelExtension->PortNumber, (Srb->SrbStatus == SRB_STATUS_SUCCESS) ? "completed successfully" : "failed"); } return; } ULONG HybridChangeByLba( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ PSTORAGE_REQUEST_BLOCK Srb ) /*++ Routine Description: IOCTL handling routine processes HYBRID request - Change by LBA. NOTE that StorAHCI only supports one LBA Range from the request. This is based on device limitation. Supporting multiple LBA Ranges using loop is not desired considering the timeout limitation. Arguments: ChannelExtension SRB Return Value: STOR Status --*/ { PAHCI_SRB_EXTENSION srbExtension; PSRB_IO_CONTROL srbControl; PDEVICE_MANAGE_DATA_SET_ATTRIBUTES dsmAttributes; PDEVICE_DSM_NVCACHE_CHANGE_PRIORITY_PARAMETERS changePriorty; PDEVICE_DATA_SET_RANGE dataSetRange; PHYBRID_CHANGE_BY_LBA_CONTEXT lbaRangeContext = NULL; ULONG logicalSectorSize; ULONG i; srbExtension = GetSrbExtension(Srb); logicalSectorSize = BytesPerLogicalSector(&ChannelExtension->DeviceExtension->DeviceParameters); srbControl = (PSRB_IO_CONTROL)SrbGetDataBuffer(Srb); dsmAttributes = (PDEVICE_MANAGE_DATA_SET_ATTRIBUTES)((PUCHAR)srbControl + ALIGN_UP(sizeof(SRB_IO_CONTROL), PVOID)); changePriorty = (PDEVICE_DSM_NVCACHE_CHANGE_PRIORITY_PARAMETERS)((PUCHAR)dsmAttributes + dsmAttributes->ParameterBlockOffset); dataSetRange = (PDEVICE_DATA_SET_RANGE)((PUCHAR)dsmAttributes + dsmAttributes->DataSetRangesOffset); if (dsmAttributes->ParameterBlockLength < sizeof(DEVICE_DSM_NVCACHE_CHANGE_PRIORITY_PARAMETERS)) { srbControl->ReturnCode = HYBRID_STATUS_INVALID_PARAMETER; Srb->SrbStatus = SRB_STATUS_BAD_SRB_BLOCK_LENGTH; return STOR_STATUS_INVALID_PARAMETER; } if ((dsmAttributes->DataSetRangesOffset < sizeof(DEVICE_MANAGE_DATA_SET_ATTRIBUTES)) || (dsmAttributes->DataSetRangesLength < sizeof(DEVICE_DATA_SET_RANGE))) { srbControl->ReturnCode = HYBRID_STATUS_ILLEGAL_REQUEST; Srb->SrbStatus = SRB_STATUS_BAD_FUNCTION; return STOR_STATUS_INVALID_PARAMETER; } if (dsmAttributes->DataSetRangesLength / sizeof(DEVICE_DATA_SET_RANGE) > GetHybridMaxLbaRangeCountForChangeLba(ChannelExtension)) { srbControl->ReturnCode = HYBRID_STATUS_ILLEGAL_REQUEST; Srb->SrbStatus = SRB_STATUS_BAD_FUNCTION; return STOR_STATUS_INVALID_PARAMETER; } if (changePriorty->TargetPriority > ChannelExtension->DeviceExtension->HybridInfo.MaximumHybridPriorityLevel) { srbControl->ReturnCode = HYBRID_STATUS_INVALID_PARAMETER; Srb->SrbStatus = SRB_STATUS_BAD_SRB_BLOCK_LENGTH; return STOR_STATUS_INVALID_PARAMETER; } // // Fail the request if NCQ is not supported. // if (!IsNCQSupported(ChannelExtension) || (ChannelExtension->StateFlags.NCQ_Activated == 0)) { srbControl->ReturnCode = HYBRID_STATUS_ILLEGAL_REQUEST; Srb->SrbStatus = SRB_STATUS_BAD_FUNCTION; return STOR_STATUS_INVALID_PARAMETER; } // // If device doesn't support Hybrid or the feature is disabled, fail the request. // if (!IsDeviceHybridInfoSupported(ChannelExtension) || !IsDeviceHybridInfoEnabled(ChannelExtension) || (ChannelExtension->DeviceExtension->SupportedCommands.HybridChangeByLbaRange == 0)) { srbControl->ReturnCode = HYBRID_STATUS_ILLEGAL_REQUEST; Srb->SrbStatus = SRB_STATUS_BAD_FUNCTION; return STOR_STATUS_INVALID_PARAMETER; } // // If device is not recognized yet, fail the request. // if (logicalSectorSize == 0) { srbControl->ReturnCode = HYBRID_STATUS_ILLEGAL_REQUEST; Srb->SrbStatus = SRB_STATUS_BAD_FUNCTION; return STOR_STATUS_INVALID_PARAMETER; } // // The request has LBA range less than 1 sector, nothing to do. // if ((dataSetRange->LengthInBytes / logicalSectorSize) == 0) { Srb->SrbStatus = SRB_STATUS_SUCCESS; return STOR_STATUS_SUCCESS; } StorPortAllocatePool(ChannelExtension->AdapterExtension, sizeof(HYBRID_CHANGE_BY_LBA_CONTEXT), AHCI_POOL_TAG, (PVOID*)&lbaRangeContext); if (lbaRangeContext == NULL) { Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; return STOR_STATUS_INSUFFICIENT_RESOURCES; } AhciZeroMemory((PCHAR)lbaRangeContext, sizeof(HYBRID_CHANGE_BY_LBA_CONTEXT)); lbaRangeContext->TargetPriority = changePriorty->TargetPriority; lbaRangeContext->DataSetRanges = dataSetRange; lbaRangeContext->DataSetRangeCount = dsmAttributes->DataSetRangesLength / sizeof(DEVICE_DATA_SET_RANGE); // // calculate how many ATA Lba entries needed to complete this request // for (i = 0; i < lbaRangeContext->DataSetRangeCount; i++) { ULONGLONG dataSetRangeLbaCount = dataSetRange[i].LengthInBytes / logicalSectorSize; // // the ATA Lba entry - SectorCount field is 16bits; // following calculation shows how many ATA Lba entries should be used to represent the dataset range entry. // if (dataSetRangeLbaCount > 0) { lbaRangeContext->NeededLbaRangeEntryCount += (ULONG)((dataSetRangeLbaCount - 1) / MAX_ATA_LBA_RANGE_SECTOR_COUNT_VALUE + 1); } } // // Prepare necessary data fields before directly calling HybridChangeByLbaCompletion(). // srbExtension->AtaFunction = ATA_FUNCTION_ATA_CFIS_PAYLOAD; srbExtension->CompletionRoutine = HybridChangeByLbaCompletion; srbExtension->CompletionContext = (PVOID)lbaRangeContext; HybridChangeByLbaCompletion(ChannelExtension, Srb); return STOR_STATUS_SUCCESS; } __inline VOID BuildHybridEvictCommand( _Inout_ PAHCI_H2D_REGISTER_FIS CFIS, _In_ USHORT BlockCount ) /*++ Routine Description: Build Hybrid Evict command to evict some Lba Ranges or whole caching medium. Arguments: CFIS - the buffer should be zero-ed before calling this function. BlockCount - value 0 indicates the whole cache should be evicted. Return Value: None --*/ { if (BlockCount > 0) { CFIS->Feature7_0 = (UCHAR)BlockCount; CFIS->Feature15_8 = (UCHAR)(BlockCount >> 8); } else { CFIS->Auxiliary7_0 = 0x1; } CFIS->Count15_8 = IDE_NCQ_SEND_HYBRID_EVICT; CFIS->Device |= (1 << 6); CFIS->Command = IDE_COMMAND_SEND_FPDMA_QUEUED; } VOID HybridEvictCompletion( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ PSTORAGE_REQUEST_BLOCK Srb ) /*++ Routine Description: Process EVICT request that received from upper layer. Arguments: ChannelExtension Srb Return Value: None --*/ { PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(Srb); BOOLEAN completed = FALSE; PHYBRID_EVICT_CONTEXT evictContext = (PHYBRID_EVICT_CONTEXT)srbExtension->CompletionContext; PCHAR buffer = (PCHAR)srbExtension->DataBuffer; ULONG bufferLength = 0; BOOLEAN tempDataSetRangeConverted = TRUE; ULONG bufferLengthUsed = 0; ULONG convertedEntryCount = 0; ULONG logicalSectorSize = BytesPerLogicalSector(&ChannelExtension->DeviceExtension->DeviceParameters); completed = ((Srb->SrbStatus != SRB_STATUS_PENDING) && (Srb->SrbStatus != SRB_STATUS_SUCCESS)) || (evictContext->ProcessedLbaRangeEntryCount >= evictContext->NeededLbaRangeEntryCount); if (!completed && (logicalSectorSize > 0)) { AhciZeroMemory(buffer, evictContext->AllocatedBufferLength); // 1. calculate the buffer size needed for EVICT command bufferLength = GetDataBufferLengthForDsmCommand(evictContext->MaxLbaRangeEntryCountPerCmd, evictContext->NeededLbaRangeEntryCount - evictContext->ProcessedLbaRangeEntryCount); // 2. prepare and send EVICT command // check if the Unmap request is satisfied while (evictContext->ProcessedLbaRangeEntryCount < evictContext->NeededLbaRangeEntryCount) { // when first time this function is called, evictContext->CurrentDataSetRange.LenthInBytes should be '0' ( < logicalSectorSize) // so that the first DataSet Range will be copied to evictContext->CurrentDataSetRange tempDataSetRangeConverted = (evictContext->CurrentRangeLbaCount == 0)? TRUE : FALSE; // if the previous entry conversion completed, continue the next one; // otherwise, still process the left part of the un-completed entry. if (tempDataSetRangeConverted) { evictContext->CurrentRangeStartLba = evictContext->DataSetRanges[evictContext->DataSetRangeIndex].StartingOffset / logicalSectorSize; evictContext->CurrentRangeLbaCount = evictContext->DataSetRanges[evictContext->DataSetRangeIndex].LengthInBytes / logicalSectorSize; evictContext->DataSetRangeIndex++; } convertedEntryCount = ConvertDataSetRangeToAtaLbaRanges(&evictContext->CurrentRangeStartLba, &evictContext->CurrentRangeLbaCount, buffer + bufferLengthUsed, bufferLength - bufferLengthUsed ); evictContext->ProcessedLbaRangeEntryCount += convertedEntryCount; bufferLengthUsed += convertedEntryCount * sizeof(ATA_LBA_RANGE); // send EVICT command when the buffer is full or all unmap entries are converted. if ( (bufferLengthUsed == bufferLength) || (evictContext->ProcessedLbaRangeEntryCount >= evictContext->NeededLbaRangeEntryCount) ) { // get ATA block count, the value is needed for setting the DSM command. USHORT transferBlockCount = (USHORT)(bufferLength / ATA_BLOCK_SIZE); // Clear the CFIS structure. AhciZeroMemory((PCHAR)&srbExtension->Cfis, sizeof(AHCI_H2D_REGISTER_FIS)); BuildHybridEvictCommand(&srbExtension->Cfis, transferBlockCount); srbExtension->AtaFunction = ATA_FUNCTION_ATA_CFIS_PAYLOAD; srbExtension->DataBuffer = buffer; srbExtension->DataTransferLength = bufferLength; srbExtension->CompletionRoutine = HybridEvictCompletion; srbExtension->CompletionContext = (PVOID)evictContext; // update the SGL to reflact the actual transfer length. srbExtension->LocalSgl.List[0].Length = bufferLength; return; } } // should not reach this path NT_ASSERT(FALSE); } else { if (!completed && (logicalSectorSize == 0)) { Srb->SrbStatus = SRB_STATUS_INVALID_LUN; } if (buffer != NULL) { AhciFreeDmaBuffer((PVOID)ChannelExtension->AdapterExtension, evictContext->AllocatedBufferLength, buffer); } if (evictContext != NULL) { StorPortFreePool((PVOID)ChannelExtension->AdapterExtension, evictContext); } srbExtension->DataBuffer = NULL; srbExtension->CompletionContext = NULL; srbExtension->CompletionRoutine = NULL; srbExtension->AtaFunction = 0; StorPortDebugPrint(3, "StorAHCI - Hybrid: Port %02d - Evict command %s. \n", ChannelExtension->PortNumber, (Srb->SrbStatus == SRB_STATUS_SUCCESS) ? "completed successfully" : "failed"); } return; } ULONG HybridEvict( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ PSTORAGE_REQUEST_BLOCK Srb ) { ULONG status = STOR_STATUS_SUCCESS; PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(Srb); PSRB_IO_CONTROL srbControl; PDEVICE_MANAGE_DATA_SET_ATTRIBUTES dsmAttributes; PDEVICE_DATA_SET_RANGE dataSetRange; PHYBRID_EVICT_CONTEXT evictContext = NULL; ULONG logicalSectorSize; ULONG i; PVOID buffer = NULL; // DMA buffer allocated for EVICT command STOR_PHYSICAL_ADDRESS bufferPhysicalAddress; logicalSectorSize = BytesPerLogicalSector(&ChannelExtension->DeviceExtension->DeviceParameters); srbControl = (PSRB_IO_CONTROL)SrbGetDataBuffer(Srb); dsmAttributes = (PDEVICE_MANAGE_DATA_SET_ATTRIBUTES)((PUCHAR)srbControl + ALIGN_UP(sizeof(SRB_IO_CONTROL), PVOID)); dataSetRange = (PDEVICE_DATA_SET_RANGE)((PUCHAR)dsmAttributes + dsmAttributes->DataSetRangesOffset); if ((dsmAttributes->Flags & DEVICE_DSM_FLAG_ENTIRE_DATA_SET_RANGE) == 0) { if ((dsmAttributes->DataSetRangesOffset < sizeof(DEVICE_MANAGE_DATA_SET_ATTRIBUTES)) || (dsmAttributes->DataSetRangesLength < sizeof(DEVICE_DATA_SET_RANGE))) { srbControl->ReturnCode = HYBRID_STATUS_ILLEGAL_REQUEST; Srb->SrbStatus = SRB_STATUS_BAD_FUNCTION; return STOR_STATUS_INVALID_PARAMETER; } } // // Fail the request if NCQ is not supported. // if (!IsNCQSupported(ChannelExtension) || (ChannelExtension->StateFlags.NCQ_Activated == 0)) { srbControl->ReturnCode = HYBRID_STATUS_ILLEGAL_REQUEST; Srb->SrbStatus = SRB_STATUS_BAD_FUNCTION; return STOR_STATUS_INVALID_PARAMETER; } // // If device doesn't support Hybrid or the feature is disabled, fail the request. // if (!IsDeviceHybridInfoSupported(ChannelExtension) || !IsDeviceHybridInfoEnabled(ChannelExtension) || (ChannelExtension->DeviceExtension->SupportedCommands.HybridEvict == 0)) { srbControl->ReturnCode = HYBRID_STATUS_ILLEGAL_REQUEST; Srb->SrbStatus = SRB_STATUS_BAD_FUNCTION; return STOR_STATUS_INVALID_PARAMETER; } // // If device is not recognized yet, fail the request. // if (logicalSectorSize == 0) { srbControl->ReturnCode = HYBRID_STATUS_ILLEGAL_REQUEST; Srb->SrbStatus = SRB_STATUS_BAD_FUNCTION; return STOR_STATUS_INVALID_PARAMETER; } // // Process EVICT request to evict the whole caching medium. // if ((dsmAttributes->Flags & DEVICE_DSM_FLAG_ENTIRE_DATA_SET_RANGE) != 0) { // // Build command to evict the whole caching medium. // BuildHybridEvictCommand(&srbExtension->Cfis, 0); srbExtension->AtaFunction = ATA_FUNCTION_ATA_CFIS_PAYLOAD; return STOR_STATUS_SUCCESS; } StorPortAllocatePool(ChannelExtension->AdapterExtension, sizeof(HYBRID_EVICT_CONTEXT), AHCI_POOL_TAG, (PVOID*)&evictContext); if (evictContext == NULL) { Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; status = STOR_STATUS_INSUFFICIENT_RESOURCES; goto Exit; } AhciZeroMemory((PCHAR)evictContext, sizeof(HYBRID_EVICT_CONTEXT)); evictContext->DataSetRanges = dataSetRange; evictContext->DataSetRangeCount = dsmAttributes->DataSetRangesLength / sizeof(DEVICE_DATA_SET_RANGE); // // calculate how many ATA Lba entries can be sent by an EVICT command // evictContext->MaxLbaRangeEntryCountPerCmd = (ChannelExtension->DeviceExtension->HybridInfo.MaximumEvictionDataBlocks * ATA_BLOCK_SIZE) / sizeof(ATA_LBA_RANGE); if (evictContext->MaxLbaRangeEntryCountPerCmd == 0) { // // Reporting "Max Lba Range Entry Count per Command" value "0" is a disk firmware bug. Use one block as the value for this case. // NT_ASSERT(FALSE); evictContext->MaxLbaRangeEntryCountPerCmd = ATA_BLOCK_SIZE / sizeof(ATA_LBA_RANGE); } // // calculate how many ATA Lba entries needed to complete this EVICT request // for (i = 0; i < evictContext->DataSetRangeCount; i++) { ULONGLONG dataSetRangeLbaCount = dataSetRange[i].LengthInBytes / logicalSectorSize; // // the ATA Lba entry - SectorCount field is 16bits; // following calculation shows how many ATA Lba entries should be used to represent the dataset range entry. if (dataSetRangeLbaCount > 0) { evictContext->NeededLbaRangeEntryCount += (ULONG)((dataSetRangeLbaCount - 1) / MAX_ATA_LBA_RANGE_SECTOR_COUNT_VALUE + 1); } } // // calculate the buffer size needed for EVICT command // evictContext->AllocatedBufferLength = GetDataBufferLengthForDsmCommand(evictContext->MaxLbaRangeEntryCountPerCmd, evictContext->NeededLbaRangeEntryCount); if (evictContext->AllocatedBufferLength == 0) { // // nothin to do. Complete the request. // Srb->SrbStatus = SRB_STATUS_SUCCESS; status = STOR_STATUS_SUCCESS; goto Exit; } // // allocate DMA buffer, this buffer will be used to store ATA LBA Ranges for EVICT command // status = AhciAllocateDmaBuffer((PVOID)ChannelExtension->AdapterExtension, evictContext->AllocatedBufferLength, &buffer); if ( (status != STOR_STATUS_SUCCESS) || (buffer == NULL) ) { Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; if (status == STOR_STATUS_SUCCESS) { status = STOR_STATUS_INSUFFICIENT_RESOURCES; } goto Exit; } // // save values before calling HybridEvictCompletion() // srbExtension->AtaFunction = ATA_FUNCTION_ATA_CFIS_PAYLOAD; srbExtension->Flags |= ATA_FLAGS_DATA_OUT; srbExtension->DataBuffer = buffer; srbExtension->DataTransferLength = evictContext->AllocatedBufferLength; srbExtension->CompletionContext = (PVOID)evictContext; bufferPhysicalAddress = StorPortGetPhysicalAddress(ChannelExtension->AdapterExtension, NULL, buffer, &i); srbExtension->LocalSgl.NumberOfElements = 1; srbExtension->LocalSgl.List[0].PhysicalAddress.LowPart = bufferPhysicalAddress.LowPart; srbExtension->LocalSgl.List[0].PhysicalAddress.HighPart = bufferPhysicalAddress.HighPart; srbExtension->LocalSgl.List[0].Length = evictContext->AllocatedBufferLength; srbExtension->Sgl = &srbExtension->LocalSgl; HybridEvictCompletion(ChannelExtension, Srb); status = STOR_STATUS_SUCCESS; Exit: // // the process failed before EVICT command can be sent. Free allocated resources. // if (status != STOR_STATUS_SUCCESS) { if (buffer != NULL) { AhciFreeDmaBuffer((PVOID)ChannelExtension->AdapterExtension, evictContext->AllocatedBufferLength, buffer); } if (evictContext != NULL) { StorPortFreePool((PVOID)ChannelExtension->AdapterExtension, evictContext); } } return status; } ULONG DsmGeneralIoctlProcess( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ PSTORAGE_REQUEST_BLOCK Srb ) /*++ Routine Description: IOCTL worker routine processes Data Set Management request from storport Arguments: ChannelExtension SRB Return Value: NT Status --*/ { ULONG status = STOR_STATUS_SUCCESS; PSRB_IO_CONTROL srbControl; PDEVICE_MANAGE_DATA_SET_ATTRIBUTES dsmAttributes; PVOID srbDataBuffer = SrbGetDataBuffer(Srb); ULONGLONG srbDataBufferLength = SrbGetDataTransferLength(Srb); // // Validate size of buffer // if (srbDataBufferLength < (ALIGN_UP(sizeof(SRB_IO_CONTROL), PVOID) + sizeof(DEVICE_MANAGE_DATA_SET_ATTRIBUTES))) { Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; return STOR_STATUS_BUFFER_TOO_SMALL; } srbControl = (PSRB_IO_CONTROL)srbDataBuffer; dsmAttributes = (PDEVICE_MANAGE_DATA_SET_ATTRIBUTES)((PUCHAR)srbControl + ALIGN_UP(sizeof(SRB_IO_CONTROL), PVOID)); if ( (srbDataBuffer == NULL) || (srbDataBufferLength < ((ULONGLONG)ALIGN_UP(sizeof(SRB_IO_CONTROL), PVOID) + dsmAttributes->DataSetRangesOffset + dsmAttributes->DataSetRangesLength)) || (srbDataBufferLength < ((ULONGLONG)ALIGN_UP(sizeof(SRB_IO_CONTROL), PVOID) + dsmAttributes->ParameterBlockOffset + dsmAttributes->ParameterBlockLength)) || (srbDataBufferLength < ((ULONGLONG)ALIGN_UP(sizeof(SRB_IO_CONTROL), PVOID) + dsmAttributes->ParameterBlockLength + dsmAttributes->DataSetRangesLength)) ) { Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; return STOR_STATUS_BUFFER_TOO_SMALL; } // // Handle the request // switch (dsmAttributes->Action) { case DeviceDsmAction_NvCache_Change_Priority: status = HybridChangeByLba(ChannelExtension, Srb); break; case DeviceDsmAction_NvCache_Evict: if ((dsmAttributes->Flags & DEVICE_DSM_FLAG_ENTIRE_DATA_SET_RANGE) != 0) { NT_ASSERT(dsmAttributes->DataSetRangesOffset == 0); NT_ASSERT(dsmAttributes->DataSetRangesLength == 0); } status = HybridEvict(ChannelExtension, Srb); break; default: Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; status = STOR_STATUS_BUFFER_TOO_SMALL; break; } return status; } #if _MSC_VER >= 1200 #pragma warning(pop) #else #pragma warning(default:4214) #pragma warning(default:4201) #pragma warning(default:26015) #endif
Our Services
-
What our customers say about us?
Read our customer testimonials to find out why our clients keep returning for their projects.
View Testimonials