VOID AhciPortBusChangeDpcRoutine( _In_ PSTOR_DPC Dpc, _In_ PVOID AdapterExtension, _In_opt_ PVOID SystemArgument1, _In_opt_ PVOID SystemArgument2 ) /*++ Asks port driver to request a QDR on behalf of the miniport It assumes: nothing Called by: Indirectly by AhciHwInterrupt It performs: 1 Kicks off the Start Channel state machine 2 Requests QDR Affected Variables/Registers: none --*/ { PAHCI_CHANNEL_EXTENSION channelExtension = (PAHCI_CHANNEL_EXTENSION)SystemArgument1; ULONG busChangeInProcess; BOOLEAN portIdle = FALSE; UNREFERENCED_PARAMETER(Dpc); UNREFERENCED_PARAMETER(AdapterExtension); UNREFERENCED_PARAMETER(SystemArgument2); if (channelExtension == NULL) { NT_ASSERT(channelExtension != NULL); return; } busChangeInProcess = InterlockedBitTestAndSet((LONG*)&channelExtension->PoFxPendingWork, 1); //BusChange is at bit 1 if (busChangeInProcess == 1) { // bus change is pending in another process. return; } RecordExecutionHistory(channelExtension, 0x00000030);//AhciPortBusChangeDpcRoutine PortAcquireActiveReference(channelExtension, NULL, &portIdle); // if port is in Active state, continue to process bus change notification. // otherwise, it will be processed when port gets into Active state if (!portIdle) { ULONG busChangePending; busChangePending = InterlockedBitTestAndReset((LONG*)&channelExtension->PoFxPendingWork, 1); //BusChange is at bit 1 if (busChangePending == 1) { PortBusChangeProcess(channelExtension); } } return; }
VOID AhciCompleteRequest( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ PSTORAGE_REQUEST_BLOCK Srb, _In_ BOOLEAN AtDIRQL ) /*++ Wrapper for ComleteRequest to protect against completing the local SRB back to port driver who doesn't know anything about local SRB It assumes: SRB is completely ready to be completed back to the SRB generator It performs: 1. If Srb has completion routine, put it in completion queue. 2. Complete the command back to the owner. Do not complete the Local Srb. Called by: AhciHwStartIo RequiresControllerResources ReleaseSlottedCommand NOTE: The caller of this routine should call AhciGetNextIos or ActiveQueue to program the command (from Srb completion routine) to adapter --*/ { PAHCI_SRB_EXTENSION srbExtension = GetSrbExtension(Srb); //1. If Srb acquired active reference or has completion routine, put it in completion queue. Otherwise, complete it. if ( ((srbExtension->Flags & ATA_FLAGS_ACTIVE_REFERENCE) != 0) || (srbExtension->CompletionRoutine != NULL) ) { STOR_LOCK_HANDLE lockhandle = {0}; if (AtDIRQL == FALSE) { AhciInterruptSpinlockAcquire(ChannelExtension->AdapterExtension, ChannelExtension->PortNumber, &lockhandle); AddQueue(ChannelExtension, &ChannelExtension->CompletionQueue, Srb, 0xDEADBEEF, 0x90); AhciInterruptSpinlockRelease(ChannelExtension->AdapterExtension, ChannelExtension->PortNumber, &lockhandle); } else { AddQueue(ChannelExtension, &ChannelExtension->CompletionQueue, Srb, 0xDEADBEEF, 0x90); } if (LogExecuteFullDetail(ChannelExtension->AdapterExtension->LogFlags)) { RecordExecutionHistory(ChannelExtension, 0x30000045); //Exit AhciCompleteRequest, SRB is put in CompletionQueue } if (!IsDumpMode(ChannelExtension->AdapterExtension)) { StorPortIssueDpc(ChannelExtension->AdapterExtension, &ChannelExtension->CompletionDpc, ChannelExtension, Srb); } else { AhciPortSrbCompletionDpcRoutine(&ChannelExtension->CompletionDpc, ChannelExtension->AdapterExtension, ChannelExtension, Srb); } } else { //2. Complete the command back to the owner if (IsMiniportInternalSrb(ChannelExtension, Srb)) { NT_ASSERT(((PSCSI_REQUEST_BLOCK)Srb == &ChannelExtension->Sense.Srb) || (ChannelExtension->StateFlags.ReservedSlotInUse == 0)); RecordExecutionHistory(ChannelExtension, 0x20000045); //Exit AhciCompleteRequest, Local SRB return; } else { NT_ASSERT(Srb->SrbStatus != SRB_STATUS_PENDING); StorPortNotification(RequestComplete, ChannelExtension->AdapterExtension, Srb); if (LogExecuteFullDetail(ChannelExtension->AdapterExtension->LogFlags)) { RecordExecutionHistory(ChannelExtension, 0x10000045); //Exit AhciCompleteRequest, SRB from port driver } } } return; }
VOID ReleaseSlottedCommand( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ UCHAR SlotNumber, _In_ BOOLEAN AtDIRQL ) /*++ Performs Completion Data marshaling back into the SRB It assumes: SRB STATUS has already been filled out ATA status from the AHCI registers is valid for this command SlotNumber points to a fully filled out Slot entry Called by: AhciHwStartIo AhciNonQueuedErrorRecovery AhciCompleteIssuedSRBs It performs: (overview) 1 Initialize 2 Marshal completion data 3 Complete the Command (details) 1.1 Keep track of the completion for our debugging records 2.1 Handle Request Sense marshaling 2.2.1 Handle everything else's marshaling 2.2.2 If this was marked as needing return data, fill in the return Task File 3.1 Make the slot available again 3.2 Perform the Slot's last request, if there was one 3.3.1 IO is completing, that IO may have paused the queue so unpause the queue 3.3.2 Complete the command Affected Variables/Registers: SRB --*/ { PSLOT_CONTENT slotContent; PAHCI_SRB_EXTENSION srbExtension; PSTORAGE_REQUEST_BLOCK srbToRelease; BOOLEAN isSenseSrb; BOOLEAN retrySrb = FALSE; slotContent = &ChannelExtension->Slot[SlotNumber]; if (slotContent->Srb == NULL) { return; } srbExtension = GetSrbExtension(slotContent->Srb); isSenseSrb = IsRequestSenseSrb(srbExtension->AtaFunction); if (LogExecuteFullDetail(ChannelExtension->AdapterExtension->LogFlags)) { RecordExecutionHistory(ChannelExtension, 0x10000053);//ReleaseSlottedCommand } //2/2 Log command completion part for our debugging records if (LogCommand(ChannelExtension->AdapterExtension->LogFlags)) { PCOMMAND_HISTORY cmdHistory; slotContent->CommandHistoryIndex %= 64; // should not be 64 or bigger. do it anyway to make sure we are safe cmdHistory = &ChannelExtension->CommandHistory[slotContent->CommandHistoryIndex]; StorPortCopyMemory((PVOID)&cmdHistory->CompletionFIS, (PVOID)&ChannelExtension->ReceivedFIS->D2hRegisterFis, sizeof(AHCI_D2H_REGISTER_FIS)); cmdHistory->CompletionPx[0] = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->CLB.AsUlong); cmdHistory->CompletionPx[1] = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->CLBU); cmdHistory->CompletionPx[2] = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->FB.AsUlong); cmdHistory->CompletionPx[3] = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->FBU); cmdHistory->CompletionPx[4] = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->IS.AsUlong); cmdHistory->CompletionPx[5] = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->IE.AsUlong); cmdHistory->CompletionPx[6] = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->CMD.AsUlong); cmdHistory->CompletionPx[7] = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->DW7_Reserved); cmdHistory->CompletionPx[8] = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->TFD.AsUlong); cmdHistory->CompletionPx[9] = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->SIG.AsUlong); cmdHistory->CompletionPx[10] = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->SSTS.AsUlong); cmdHistory->CompletionPx[11] = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->SCTL.AsUlong); cmdHistory->CompletionPx[12] = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->SERR.AsUlong); cmdHistory->CompletionPx[13] = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->SACT); cmdHistory->CompletionPx[14] = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->CI); cmdHistory->CompletionPx[15] = StorPortReadRegisterUlong(ChannelExtension->AdapterExtension, &ChannelExtension->Px->SNTF.AsUlong); cmdHistory->SrbStatus = slotContent->Srb->SrbStatus; } //2. Then complete the command if (isSenseSrb) { //2.1 Handle Request Sense marshaling srbToRelease = (PSTORAGE_REQUEST_BLOCK)SrbGetOriginalRequest(slotContent->Srb); //that original SRB must have SRB_STATUS_AUTOSENSE_VALID set appropriately if (slotContent->Srb->SrbStatus == SRB_STATUS_SUCCESS) { srbToRelease->SrbStatus |= SRB_STATUS_AUTOSENSE_VALID; SrbSetScsiStatus(srbToRelease, SCSISTAT_CHECK_CONDITION); } else { srbToRelease->SrbStatus &= ~SRB_STATUS_AUTOSENSE_VALID; } //finish use of Sense.Srb srbExtension->AtaFunction = 0; } else { //2.2.1 Handle everything else's marshaling srbToRelease = slotContent->Srb; if ((srbExtension->Flags & ATA_FLAGS_SENSEDATA_SET) == 0) { //Record error and status srbExtension->AtaStatus = ChannelExtension->TaskFileData.STS.AsUchar; srbExtension->AtaError = ChannelExtension->TaskFileData.ERR; //2.2.2 If this was marked as needing return data, fill in the return Task File. if( IsReturnResults(srbExtension->Flags) ) { SetReturnRegisterValues(ChannelExtension, srbToRelease, NULL); } // interpret ATA error to be SCSI error and fill SenseBuffer // also log IO Record if (IsAtaCommand(srbExtension->AtaFunction) || IsAtaCfisPayload(srbExtension->AtaFunction)) { // IO Record for ATA device is logged in AtaMapError() AtaMapError(ChannelExtension, srbToRelease, slotContent->StateFlags.FUA); } else { if (SRB_STATUS(srbToRelease->SrbStatus) == SRB_STATUS_SUCCESS) { ChannelExtension->DeviceExtension[0].IoRecord.SuccessCount++; } else { ChannelExtension->DeviceExtension[0].IoRecord.OtherErrorCount++; } } } } //3.1 Make the slot available again slotContent->CmdHeader = NULL; slotContent->CommandHistoryIndex = 0; slotContent->Srb = NULL; slotContent->StateFlags.FUA = FALSE; //Clear the CommandsToComplete bit ChannelExtension->SlotManager.CommandsToComplete &= ~(1 << SlotNumber); ChannelExtension->SlotManager.HighPriorityAttribute &= ~(1 << SlotNumber); //3.3.1 IO is completing, that IO may have paused the queue so unpause the queue // During NCQ Error Recovery process, this flag will be reset in the error recovery completion process, either through reset or through slot release again. if (ChannelExtension->StateFlags.NcqErrorRecoveryInProcess == 0) { ChannelExtension->StateFlags.QueuePaused = FALSE; } //3.3.2 Complete the command if ( (srbToRelease->SrbStatus != SRB_STATUS_SUCCESS) && !isSenseSrb && (srbExtension->RetryCount == 0) ) { if ( (ChannelExtension->StateFlags.HybridInfoEnabledOnHiberFile == 1) && IsNCQWriteCommand(srbExtension) ) { // NCQ WRITE with Hybrid Information failed, retry once without Hybrid Information. ChannelExtension->StateFlags.HybridInfoEnabledOnHiberFile = 0; retrySrb = TRUE; } else if (srbToRelease->SrbStatus == SRB_STATUS_PARITY_ERROR) { // if a command encounters CRC error, retry once retrySrb = TRUE; } } if (retrySrb) { srbToRelease->SrbStatus = SRB_STATUS_PENDING; srbExtension->RetryCount++; AhciProcessIo(ChannelExtension, srbToRelease, AtDIRQL); } else { // otherwise, complete it. AhciCompleteRequest(ChannelExtension, srbToRelease, AtDIRQL); } }
VOID P_Running_WaitOnFRE( PAHCI_CHANNEL_EXTENSION ChannelExtension ) /* Start the Receive buffer phase Wait for confirmation is a 'nice to have' but isn't necessary. Called By: P_Running It assumes: DET == 3 or IPM == 02 or 06 It performs: (overview) 1 Set FRE 2 Wait for 50ms for FR to reflect running status 3 Move to WaitOnBSYDRQ (details) 1.1 Set FRE 2.1 Wait for 50ms for FR to reflect running status 3.1 Move to WaitOnBSYDRQ Affected Variables/Registers: CMD */ { AHCI_COMMAND cmd; WaitOnFRE_Start: #ifdef ENABLE_HISTORY_LOG RecordExecutionHistory(ChannelExtension, 0x00000019);//P_Running_FRE #endif //1.1 Set FRE cmd.AsUlong = AtaPortReadRegisterUlong(&ChannelExtension->Px->CMD.AsUlong); cmd.FRE = 1; AtaPortWriteRegisterUlong(&ChannelExtension->Px->CMD.AsUlong, cmd.AsUlong); cmd.AsUlong = AtaPortReadRegisterUlong(&ChannelExtension->Px->CMD.AsUlong); if (cmd.FR == 1) { //3.1 Move to WaitOnBSYDRQ ChannelExtension->StartState.ChannelNextStartState = WaitOnBSYDRQ; RecordExecutionHistory(ChannelExtension, 0x000a0019);//P_Running_WaitOnFRE Success P_Running_WaitOnBSYDRQ(ChannelExtension); return; } else { //3.1 Move to WaitOnBSYDRQ if (ChannelExtension->StartState.ChannelStateFRECount > 5) { ChannelExtension->StartState.ChannelNextStartState = WaitOnBSYDRQ; RecordExecutionHistory(ChannelExtension, 0x00ff0019);//P_Running_WaitOnFRE timed out P_Running_WaitOnBSYDRQ(ChannelExtension); return; //2.1 Wait for 50ms for FR to reflect running status } else { ChannelExtension->StartState.ChannelStateFRECount++; RecordExecutionHistory(ChannelExtension, 0x00080019);//P_Running_WaitOnFRE still waiting if (ChannelExtension->StateFlags.Crashing) { AtaPortStallExecution(10000); //10 milliseconds goto WaitOnFRE_Start; } else { if(! AtaPortRequestTimer(ChannelExtension, P_Running, 10000)) { //10 milliseconds AtaPortStallExecution(10000); //10 milliseconds goto WaitOnFRE_Start; } } return; } } }
VOID P_Running_WaitOnDET3( PAHCI_CHANNEL_EXTENSION ChannelExtension ) /* Done monkeying phase. From here on out only a DET of 3 will do Called By: P_Running It assumes: Channel is start capable It performs: (overview) 1 Look for signs of life which are DET == 3 (details) 1.1 Wait for 1 second until DET becomes 3 1.2 After waiting for 1 second, give up on starting the channel 1.3 If DET==3 we are ready for WaitOnFRE Affected Variables/Registers: none */ { AHCI_SERIAL_ATA_STATUS ssts; WaitOnDET3_Start: #ifdef ENABLE_HISTORY_LOG RecordExecutionHistory(ChannelExtension, 0x00000018);//P_Running_WaitOnDET3 #endif //1.2 After waiting for 1 second, give up on starting the channel ssts.AsUlong = AtaPortReadRegisterUlong(&ChannelExtension->Px->SSTS.AsUlong); if (ssts.DET != 3) { if (ChannelExtension->StartState.ChannelStateDET3Count > 100) { P_Running_StartFailed(ChannelExtension); RecordExecutionHistory(ChannelExtension, 0x10ff0018);//P_Running_WaitOnDET3 timed out return; //1.1 Wait for 1 second until DET becomes 3 } else { ChannelExtension->StartState.ChannelStateDET3Count++; RecordExecutionHistory(ChannelExtension, 0x10080018);//P_Running_WaitOnDET3 still waiting if (ChannelExtension->StateFlags.Crashing) { AtaPortStallExecution(10000); //10 milliseconds goto WaitOnDET3_Start; } else { if(!AtaPortRequestTimer(ChannelExtension, P_Running, 10000)){ //10 milliseconds AtaPortStallExecution(10000); //10 milliseconds goto WaitOnDET3_Start; } } return; } //1.3 If DET==3 we are ready for WaitOnFRE } else { ChannelExtension->StartState.ChannelNextStartState = WaitOnFRE; RecordExecutionHistory(ChannelExtension, 0x100a0018);//P_Running_WaitOnDET3 Success P_Running_WaitOnFRE(ChannelExtension); return; } }
VOID AhciNonQueuedErrorRecovery( PAHCI_CHANNEL_EXTENSION ChannelExtension ) { /* AHCI 1.1 Section 6.2.2.1 "The flow for system software to recover from an error when non-queued commands are issued is as follows: Reads PxCI to see which commands are still outstanding Reads PxCMD.CCS to determine the slot that the HBA was processing when the error occurred Clears PxCMD.ST to ‘0’ to reset the PxCI register, waits for PxCMD.CR to clear to ‘0’ Clears any error bits in PxSERR to enable capturing new errors. Clears status bits in PxIS as appropriate If PxTFD.STS.BSY or PxTFD.STS.DRQ is set to ‘1’, issue a COMRESET to the device to put it in an idle state Sets PxCMD.ST to ‘1’ to enable issuing new commands It assumes: Called asynchronously Called by: WorkerDispatch It performs: (overview) 1 Recover from an error 2 Complete the Failed Command 3 Complete Succeeded Commands (details) 1.1 Initialize 1.2 Reads PxCI to see which commands are still outstanding 1.3 Reads PxCMD.CCS to determine the slot that the HBA was processing when the error occurred 1.4.1 Clears PxCMD.ST to ‘0’ to reset the PxCI register, waits for PxCMD.CR to clear to ‘0’ 1.5 Clears any error bits in PxSERR to enable capturing new errors. 1.6 Clears status bits in PxIS as appropriate 1.4.2 If PxTFD.STS.BSY or PxTFD.STS.DRQ is set to ‘1’, issue a COMRESET to the device to put it in an idle state 1.4.3 If a COMRESET was issued, restore Preserved Settings 1.4.4 Start the channel 2.1 Complete the command being issued if there was one 2.2 Restore the unsent programmed commands for careful reprocessing 2.3 If a request sense IRB was created due to this error 3.1 If there were commands that are ready to complete, complete them now Affected Variables/Registers: CI, CMD, TFD Channel Extension */ BOOLEAN performedCOMRESET; ULONG ci; ULONG localCommandsIssued; UCHAR numberCommandsOutstanding; ULONG failingCommand; AHCI_COMMAND cmd; AHCI_TASK_FILE_DATA tfd; PIDE_REQUEST_BLOCK senseIrb; PAHCI_CHANNEL_EXTENSION channelExtension = (PAHCI_CHANNEL_EXTENSION) ChannelExtension; senseIrb = NULL; //1.1 Initialize variables #ifdef ENABLE_HISTORY_LOG RecordExecutionHistory(channelExtension, 0x00000013);//AhciNonQueuedErrorRecovery #endif performedCOMRESET = FALSE; //1.2 Reads PxCI to see which commands are still outstanding ci = AtaPortReadRegisterUlong(&channelExtension->Px->CI); //1.3 Reads PxCMD.CCS to determine the slot that the HBA was processing when the error occurred cmd.AsUlong = AtaPortReadRegisterUlong(&channelExtension->Px->CMD.AsUlong); //1.4 Clears PxCMD.ST to ‘0’ to reset the PxCI register, waits for PxCMD.CR to clear to ‘0’ if (! P_NotRunning(channelExtension, channelExtension->Px, FALSE) ){ //This clears PxCI RecordExecutionHistory(channelExtension, 0x10160013);//Stop Failed channelExtension->StateFlags.CallAhciReset = TRUE; if(!AtaPortRequestSynchronizedRoutine(channelExtension, WorkerDispatch)){ WorkerDispatch(channelExtension); } return; } //2.1 Complete the command being issued if there was one //Determine how many commands are outstanding localCommandsIssued = channelExtension->SlotManager.CommandsIssued; numberCommandsOutstanding = 0; failingCommand = 0; while (localCommandsIssued) { if (localCommandsIssued & 1) { numberCommandsOutstanding++; } //To keep from having to walk through the localCommandsIssued mask again, //if there is only 1 command, failing commands will be 1 greater than that slot after this while loop. failingCommand++; localCommandsIssued >>= 1; } //If there is only one command outstanding, then we know which one to complete. if ( numberCommandsOutstanding == 1) { failingCommand--; //As long as there is an IRB in that command slot. if (! channelExtension->Slot[failingCommand].Irb ) { AhciHwReset(channelExtension); return; } //If NonQueuedErrorRecovery should not be done for more than 1 command stop here. } else if ( (cmd.ST == 1) && ( channelExtension->StateFlags.NCQ_NeverNonQueuedErrorRecovery ) ) { AhciHwReset(channelExtension); return; } else { //otherwise the failing command is in CCS, as long as there is an IRB in that command slot. if( ((channelExtension->SlotManager.CommandsIssued & (1 << cmd.CCS)) > 0) && (channelExtension->Slot[cmd.CCS].Irb) ) { failingCommand = cmd.CCS; } else { AhciHwReset(channelExtension); return; } } // Keep in mind although Px.CMD.ST was just shut off, we are working with the previous snapshot of ST. if (cmd.ST == 1) { // Remove the errant command from the Commands Issued. ci &= ~(1 << failingCommand); // Move the errant command from Issued list to the 'to complete' list channelExtension->SlotManager.CommandsIssued &= ~(1 << failingCommand); channelExtension->SlotManager.HighPriorityAttribute &= ~(1 << failingCommand); channelExtension->SlotManager.CommandsToComplete |= (1 << failingCommand); // Fill in the status of the command channelExtension->Slot[failingCommand].Irb->AtaStatus = AtaPortReadRegisterUchar(&channelExtension->Px->TFD.STS.AsUchar); channelExtension->Slot[failingCommand].Irb->AtaError = AtaPortReadRegisterUchar(&channelExtension->Px->TFD.ERR); channelExtension->Slot[failingCommand].Irb->IrbStatus = IRB_STATUS_DEVICE_ERROR; // Handle Request Sense if needed if( NeedRequestSense(channelExtension->Slot[failingCommand].Irb) ) { senseIrb = AtaPortBuildRequestSenseIrb (channelExtension, channelExtension->Slot[failingCommand].Irb); if (senseIrb) { senseIrb->NextIrb = channelExtension->Slot[failingCommand].Irb; //Make the slot available again now, its slot content will not be cleaned up later. channelExtension->Slot[failingCommand].CmdHeader = NULL; channelExtension->Slot[failingCommand].CommandHistoryIndex = 0; channelExtension->Slot[failingCommand].CompletionRoutine = NULL; channelExtension->Slot[failingCommand].Irb = NULL; //Do not complete this command normally, it will be completed when it's request sense is completed channelExtension->SlotManager.CommandsToComplete &= ~(1 << failingCommand); RecordExecutionHistory(channelExtension, 0x10730013);//NonNCQ Error Received RequestSense } else { // If the senseIrb could not be acquired, complete the command manually channelExtension->Slot[failingCommand].Irb->IrbStatus &= ~IRB_STATUS_AUTOSENSE_VALID; ReleaseSlottedCommand(channelExtension, (UCHAR) failingCommand);//&channelExtension->Slot[cmd.CCS]); //failed IRB RecordExecutionHistory(channelExtension, 0x10830013);//NonNCQ Error Failed to get RequestSense } }else{ // Fill in the status of the command channelExtension->Slot[failingCommand].Irb->AtaStatus = 0x51; channelExtension->Slot[failingCommand].Irb->AtaError = 0x04; channelExtension->Slot[failingCommand].Irb->IrbStatus = IRB_STATUS_DEVICE_ERROR; // Complete the command manually ReleaseSlottedCommand(channelExtension, (UCHAR)failingCommand); //&channelExtension->Slot[cmd.CCS] ); ActivateQueue(ChannelExtension); RecordExecutionHistory(channelExtension, 0x10630013);//NonNCQ Error Completed failed command } } //1.5 Clears any error bits in PxSERR to enable capturing new errors. //Handled in the Interrupt routine //1.6 Clears status bits in PxIS as appropriate //Handled in the Interrupt routine //1.4.2 If PxTFD.STS.BSY or PxTFD.STS.DRQ is set to ‘1’, issue a COMRESET to the device to put it in an idle state tfd.AsUlong = AtaPortReadRegisterUlong(&channelExtension->Px->TFD.AsUlong); if(tfd.STS.BSY || tfd.STS.DRQ) { AhciCOMRESET(channelExtension, channelExtension->Px); performedCOMRESET = TRUE; } //2.2 Restore the unsent programmed commands for careful reprocessing channelExtension->SlotManager.NormalQueueSlice |= ci; //put the commands that didn't get a chance to finish back into the normal queue channelExtension->SlotManager.CommandsIssued &= ~ci;//Remove the unfinished commands from the 'issued' list //2.3 If a request sense IRB was created due to this error if (senseIrb){ ChannelExtension->StateFlags.QueuePaused = FALSE; AhciHwStartIo(channelExtension, senseIrb); } //3.1 If there were commands that are ready to complete, complete them if (channelExtension->SlotManager.CommandsToComplete) { AhciCompleteIssuedIRBs(channelExtension, IRB_STATUS_SUCCESS); //complete the successful commands to start the RS IRB. } //1.4.3 If a COMRESET was issued, restore Preserved Settings if (performedCOMRESET == TRUE) { RestorePreservedSettings(ChannelExtension); } //1.4.4 Start the channel P_Running_StartAttempt(ChannelExtension); RecordExecutionHistory(channelExtension, 0x10000013);//Exit AhciNonQueuedErrorRecovery }
VOID P_Running_WaitOnDET( PAHCI_CHANNEL_EXTENSION ChannelExtension ) /* Search for Device Activity phase. Use DET and IPM for any signs of life as defined in AHCI 1.2 section 10.1.2 and 10.3.1. Polled 100 times in 1 second Called By: P_Running It assumes: Controller is start capable It performs: (overview) 1 Look for signs of life while DET == 0 2 Look for signs of life while DET == 1 3 Look for signs of life which is DET == 3 (details) 1.1 When looking for device presence, seeing link level power managment shall count as device present as per 10.3.1 1.2 Wait for 1 second for DET to show signs of life 1.3 After 1 second give up 2.1 When looking for device presence, seeing link level power managment shall count as device present as per 10.3.1 2.2 Otherwise continue on to WaitOnDET1 3.1 If DET==3 we are ready for WaitOnFRE already. Affected Variables/Registers: none */ { AHCI_SERIAL_ATA_STATUS ssts; WaitOnDET_Start: #ifdef ENABLE_HISTORY_LOG RecordExecutionHistory(ChannelExtension, 0x00000016);//P_Running_WaitOnDET #endif //1 Look for signs of life while DET == 0 ssts.AsUlong = AtaPortReadRegisterUlong(&ChannelExtension->Px->SSTS.AsUlong); if (ssts.DET == 0) { //When a COMRESET is sent to the device the PxSSTS.DET field shall be cleared to 0h. //1.1 When looking for device presence, seeing link level power managment shall count as device present as per 10.3.1 if ((ssts.IPM == 0x2) || (ssts.IPM == 0x6)) { ChannelExtension->StartState.ChannelNextStartState = WaitOnFRE; RecordExecutionHistory(ChannelExtension, 0x10800016);//P_Running_WaitOnDET is 0 w/ LPM activity, goto FRE P_Running_WaitOnFRE(ChannelExtension); return; //1.3 After 1 second give up } else if (ChannelExtension->StartState.ChannelStateDETCount > 100) { P_Running_StartFailed(ChannelExtension); RecordExecutionHistory(ChannelExtension, 0x10ff0016);//P_Running_WaitOnDET Timed out, No Device return; //1.2 Wait for 1 second for DET to show signs of life } else { ChannelExtension->StartState.ChannelStateDETCount++; RecordExecutionHistory(ChannelExtension, 0x10810016);//P_Running_WaitOnDET is 0, still waiting if (ChannelExtension->StateFlags.Crashing) { AtaPortStallExecution(10000); //10 milliseconds goto WaitOnDET_Start; } else { if(! AtaPortRequestTimer(ChannelExtension, P_Running, 10000)) { //10 milliseconds AtaPortStallExecution(10000); //10 milliseconds goto WaitOnDET_Start; } } return; } } else { //2 Look for signs of life while DET == 1 if (ssts.DET == 1) { //2.1 When looking for device presence, seeing link level power managment shall count as device present as per 10.3.1 if ((ssts.IPM == 0x2) || (ssts.IPM == 0x6)) { ChannelExtension->StartState.ChannelNextStartState = WaitOnFRE; RecordExecutionHistory(ChannelExtension, 0x10a00016);//P_Running_WaitOnDET is 1 w/ LPM activity, goto FRE P_Running_WaitOnFRE(ChannelExtension); return; //2.2 Otherwise continue on to WaitOnDET1 } else { ChannelExtension->StartState.ChannelNextStartState = WaitWhileDET1; RecordExecutionHistory(ChannelExtension, 0x10a10016);//P_Running_WaitOnDET is 1 P_Running_WaitWhileDET1(ChannelExtension); return; } //3.1 If DET==3 we are ready for WaitOnFRE already. } else if (ssts.DET == 3) { ChannelExtension->StartState.ChannelNextStartState = WaitOnFRE; RecordExecutionHistory(ChannelExtension, 0x10a20016);//P_Running_WaitOnDET is 3 P_Running_WaitOnFRE(ChannelExtension); return; } else { P_Running_StartFailed(ChannelExtension); RecordExecutionHistory(ChannelExtension, 0x10a30016);//P_Running_WaitOnDET is Bogus, aborting return; } } }
VOID P_Running_WaitWhileDET1( PAHCI_CHANNEL_EXTENSION ChannelExtension ) /* Waiting on establishment of link level communications phase. A '1' should become a '3'. If it doesn't, help it along. NOTE: When a COMINIT is received, the PxSSTS.DET field shall be set to 1h. That means a device is detected, but communications has not finished Polled 100 times in 1 second Called By: P_Running It assumes: Channel is start capable DET was previously the value of 1 It performs: (overview) 1 Look for signs of life which is DET == 3 2 Look for signs of live while DET == 1 (details) 1.1 If DET moves from 1 to 3, go to WaitOnFRE 2.1 Wait for 1 second for DET to become 3 2.2 After 1 second of waiting, force the controller to 150MB/s speeds and go to WaitOnDET3 Affected Variables/Registers: SCTL, SERR */ { AHCI_SERIAL_ATA_STATUS ssts; AHCI_SERIAL_ATA_CONTROL sctl; AHCI_SERIAL_ATA_ERROR serr; WaitWhileDET1_Start: #ifdef ENABLE_HISTORY_LOG RecordExecutionHistory(ChannelExtension, 0x00000017);//P_Running_WaitWhileDET1 #endif //1.1 If DET moves from 1 to 3, go to WaitOnFRE ssts.AsUlong = AtaPortReadRegisterUlong(&ChannelExtension->Px->SSTS.AsUlong); if (ssts.DET == 3) { ChannelExtension->StartState.ChannelNextStartState = WaitOnFRE; RecordExecutionHistory(ChannelExtension, 0x10a20017);//P_Running_WaitWhileDET1 done P_Running_WaitOnFRE(ChannelExtension); return; } else { //2.2 After 1 second of waiting, force the controller to 150MB/s speeds and go to WaitOnDET3 if (ChannelExtension->StartState.ChannelStateDET1Count > 100) { //A very wise woman once taught me this trick //it is possible that the device is not handling speed negotation very well //help it out by allowing only 150MB/s sctl.AsUlong = AtaPortReadRegisterUlong(&ChannelExtension->Px->SCTL.AsUlong); sctl.SPD = 1; AtaPortWriteRegisterUlong(&ChannelExtension->Px->SCTL.AsUlong, sctl.AsUlong); //Reset the port AhciCOMRESET(ChannelExtension, ChannelExtension->Px); //and clear out SERR serr.AsUlong = (ULONG)~0; AtaPortWriteRegisterUlong(&ChannelExtension->Px->SERR.AsUlong, serr.AsUlong); ChannelExtension->StartState.ChannelNextStartState = WaitOnDET3; RecordExecutionHistory(ChannelExtension, 0x100a0017);//P_Running_WaitWhileDET1 timed out, speed stepping down if (ChannelExtension->StateFlags.Crashing) { AtaPortStallExecution(10000); //10 milliseconds P_Running_WaitOnDET3(ChannelExtension); } else { if(! AtaPortRequestTimer(ChannelExtension, P_Running, 10000)){ //10 milliseconds AtaPortStallExecution(10000); //10 milliseconds P_Running_WaitOnDET3(ChannelExtension); } } return; //2.1 Wait for DET to become 3 for 1 second } else { ChannelExtension->StartState.ChannelStateDET1Count++; RecordExecutionHistory(ChannelExtension, 0x10600017);//P_Running_WaitWhileDET1 still waiting if (ChannelExtension->StateFlags.Crashing) { AtaPortStallExecution(10000); //10 milliseconds goto WaitWhileDET1_Start; } else { if(! AtaPortRequestTimer(ChannelExtension, P_Running, 10000)){ //10 milliseconds AtaPortStallExecution(10000); //10 milliseconds goto WaitWhileDET1_Start; } } return; } } }
BOOLEAN P_Running( PAHCI_CHANNEL_EXTENSION ChannelExtension ) /* The purpose of this function is to verify and drive the Start Channel state machine Called By: P_Running_StartAttempt Through AtaPortRequestTimer which is set up by each of the Start Channel state functions: P_Running_WaitOnDET P_Running_WaitWhileDET1 P_Running_WaitOnDET3 P_Running_WaitOnFRE P_Running_WaitOnBSYDRQ It assumes: Nothing It performs: (overview) 1 Verify Start can/needs to be done 2 Run the Start Channel state machine (details) 1.1 First, is the channel initialized. Would turning start on blow up the machine? 1.2 Next, is the port somehow already running? 1.3 Make sure the device knows it is supposed to be spun up 1.4 Check to make sure that FR and CR are both 0. Attempt to bring the controller into a consistent state by stopping the controller. 1.5 CMD.ST has to be set, when that happens check to see that PxSSTS.DET is not ‘4h’ 2.1 Dispatch to the current state (states are responsible for selecting the next state) Affected Variables/Registers: CMD Return Values: TRUE if the state machine can be run FALSE if the state machine can't */ { AHCI_COMMAND cmd; AHCI_SERIAL_ATA_STATUS ssts; PAHCI_PORT px = ChannelExtension->Px; #ifdef ENABLE_HISTORY_LOG RecordExecutionHistory(ChannelExtension, 0x00000015);//P_Running #endif //1.1 First, is the channel initialized. Would turning start on blow up the machine? if( ChannelExtension->StateFlags.StartCapable == FALSE ) { RecordExecutionHistory(ChannelExtension, 0x10120015);//No Channel Resources return FALSE; } //1.2 Next, is the port somehow already running? cmd.AsUlong = AtaPortReadRegisterUlong(&px->CMD.AsUlong); if( (cmd.ST == 1) && (cmd.CR == 1) && (cmd.FRE == 1) && (cmd.FR == 1) ) { ChannelExtension->StartState.ChannelNextStartState = StartComplete; #ifdef ENABLE_HISTORY_LOG RecordExecutionHistory(ChannelExtension, 0x30000015);//Channel Already Running #endif return TRUE; } //1.3 Make sure the device knows it is supposed to be spun up cmd.SUD = 1; AtaPortWriteRegisterUlong(&px->CMD.AsUlong, cmd.AsUlong); //1.4 Check to make sure that FR and CR are both 0. If not then ST and/or FRE are 0 which a bad scenario. if ( ( (cmd.FR == 1) && (cmd.FRE == 0) ) || ( (cmd.CR == 1) && (cmd.ST == 0) ) ){ //Attempt to bring the controller into a consistent state by stopping the controller. if ( !P_NotRunning(ChannelExtension, ChannelExtension->Px, FALSE) ) { RecordExecutionHistory(ChannelExtension, 0x10880015);//CR or FR 1 when ST or FRE 0 //return FALSE; } } //1.5 CMD.ST has to be set, when that happens check to see that PxSSTS.DET is not ‘4h’ ssts.AsUlong = AtaPortReadRegisterUlong(&px->SSTS.AsUlong); if( ssts.DET == 0x4){ P_Running_StartFailed(ChannelExtension); RecordExecutionHistory(ChannelExtension, 0x10150015);//Channel Disabled return FALSE; } //2.1 Ok, run the Start Channel State Machine switch(ChannelExtension->StartState.ChannelNextStartState) { case WaitOnDET: P_Running_WaitOnDET(ChannelExtension); break; case WaitWhileDET1: P_Running_WaitWhileDET1(ChannelExtension); break; case WaitOnDET3: P_Running_WaitOnDET3(ChannelExtension); break; case WaitOnFRE: P_Running_WaitOnFRE(ChannelExtension); break; case WaitOnBSYDRQ: P_Running_WaitOnBSYDRQ(ChannelExtension); break; default: break; } // record this function is called. RecordExecutionHistory(ChannelExtension, 0x10000015);//Exit P_Running return TRUE; }
BOOLEAN P_NotRunning( PAHCI_CHANNEL_EXTENSION ChannelExtension, PAHCI_PORT Px, BOOLEAN InReset ) /* Called By: AhciNonQueuedErrorRecovery AhciHwReset P_Running AhciHwControlIdeStart AhciHwControlIdeStop AhciHwControlIdePowerDown It assumes: nothing It performs: 5.3.2.3 P:NotRunning (overview) 1 Clear CMD.ST 2 Clear CMD.FRE 3 Update the Channel Start State (details) 1.1 Clear CMD.ST 1.2 Verify CR cleared 1.3 Verify CI cleared 2.1 Clear CMD.FRE 2.2 Verify FR cleared 3.1 Update the Channel Start State Affected Variables,Registers: cmd.ST cmd.CR cmd.FRE cmd.FR are 0 and the controller is in P:NotRunning Return value: FALSE if the ST,CR,FRE, and FRE could not be cleared. TRUE if success. */ { AHCI_COMMAND cmd; ULONG ci; UCHAR i; AHCI_TASK_FILE_DATA tfd; BOOLEAN enableCLO = FALSE; // record this function is called. if (ChannelExtension) { RecordExecutionHistory(ChannelExtension, 0x00000011);//P_NotRunning if ( (InReset == TRUE) && (ChannelExtension->CAP.SCLO == TRUE) && (ChannelExtension->StateFlags.CLOReset_Enable) ) { //3.1.1 //make sure HBA supports Command List Override. enableCLO = TRUE; RecordExecutionHistory(ChannelExtension, (0x10810011) ); } } //1.1 Clear CMD.ST cmd.AsUlong = AtaPortReadRegisterUlong(&Px->CMD.AsUlong); //10.3.2 on FRE it says: //Software shall not clear this [FRE] bit while PxCMD.ST remains set to ‘1’." //System software places a port into the idle state by clearing PxCMD.ST and waiting for PxCMD.CR to return ‘0’ when read. cmd.ST = 0; AtaPortWriteRegisterUlong(&Px->CMD.AsUlong, cmd.AsUlong); if ( enableCLO ) { //3.3.7 //make sure PxCMD.ST is 0. cmd.AsUlong = AtaPortReadRegisterUlong(&Px->CMD.AsUlong); for (i=1; i < 101; i++) { cmd.AsUlong = AtaPortReadRegisterUlong(&Px->CMD.AsUlong); if (cmd.ST == 0) { break; } AtaPortStallExecution(5000); //5 milliseconds } if (i == 101) { RecordExecutionHistory(ChannelExtension, (0x10820011 | (i << 8)) ); } tfd.AsUlong = AtaPortReadRegisterUlong(&ChannelExtension->Px->TFD.AsUlong); if (tfd.STS.BSY) { cmd.CLO = 1; AtaPortWriteRegisterUlong(&Px->CMD.AsUlong, cmd.AsUlong); } } //Software should wait at least 500 milliseconds for this to occur. for (i=1; i < 101; i++) { cmd.AsUlong = AtaPortReadRegisterUlong(&Px->CMD.AsUlong); if ( (cmd.CR == 0) && (cmd.ST == 0) ) { break; } AtaPortStallExecution(5000); //5 milliseconds } #ifdef ENABLE_HISTORY_LOG if (ChannelExtension) { RecordExecutionHistory(ChannelExtension, (0x10340011 | (i << 8)) ); } #endif //If PxCMD.CR or PxCMD.FR do not clear to ‘0’ correctly, then software may attempt a port reset or a full HBA reset to recover. if (i == 101) { return FALSE; } //1.2 Verify CR cleared //This must have the effect of clearling CI as per section 3.3.14 for (i=1; i < 101; i++) { ci = AtaPortReadRegisterUlong(&Px->CI); if ( ci == 0 ) { break; } else { cmd.ST = 0; AtaPortWriteRegisterUlong(&Px->CMD.AsUlong, cmd.AsUlong); } AtaPortStallExecution(50); //50 microseconds } #ifdef ENABLE_HISTORY_LOG if (ChannelExtension) { RecordExecutionHistory(ChannelExtension, (0x10840011 | (i << 8)) ); } #endif //If CI does not clear to ‘0’ correctly abort the stop if (i == 101) { return FALSE; } //2.1 Clear CMD.FRE cmd.AsUlong = AtaPortReadRegisterUlong(&Px->CMD.AsUlong); if( (cmd.FRE || cmd.FR) != 0){ //If PxCMD.FRE is set to ‘1’, software should clear it to ‘0’ and wait at least 500 milliseconds for PxCMD.FR to return ‘0’ when read. //10.3.2 Software shall not clear this bit while PxCMD.ST or PxCMD.CR is set to ‘1’. cmd.FRE = 0; AtaPortWriteRegisterUlong(&Px->CMD.AsUlong, cmd.AsUlong); if ( enableCLO ) { tfd.AsUlong = AtaPortReadRegisterUlong(&ChannelExtension->Px->TFD.AsUlong); if (tfd.STS.BSY) { cmd.CLO = 1; AtaPortWriteRegisterUlong(&Px->CMD.AsUlong, cmd.AsUlong); } } //Software should wait at least 500 milliseconds for this to occur. for (i=1; i<101; i++) { cmd.AsUlong = AtaPortReadRegisterUlong(&Px->CMD.AsUlong); if( (cmd.CR == 0) && (cmd.FR == 0) && (cmd.ST == 0) && (cmd.FRE == 0) ) { break; } AtaPortStallExecution(5000); // 5 milliseconds } #ifdef ENABLE_HISTORY_LOG if (ChannelExtension) { RecordExecutionHistory(ChannelExtension, (0x10330011 | (i << 8)) ); } #endif if (i == 101) { //If PxCMD.CR or PxCMD.FR do not clear to ‘0’ correctly, then software may attempt a port reset or a full HBA reset to recover. if ( enableCLO ) { //if we return caller will start COMRESET. return TRUE; } return FALSE; } } //3.1 Update the Channel Start State ChannelExtension->StartState.ChannelNextStartState = WaitOnDET; ChannelExtension->StartState.ChannelStateDETCount = 0; ChannelExtension->StartState.ChannelStateDET1Count = 0; ChannelExtension->StartState.ChannelStateDET3Count = 0; ChannelExtension->StartState.ChannelStateFRECount = 0; ChannelExtension->StartState.ChannelStateBSYDRQCount = 0; #ifdef ENABLE_HISTORY_LOG if (ChannelExtension) { RecordExecutionHistory(ChannelExtension, 0x10000011);//Exit P_NotRunning } #endif return TRUE; }
VOID AhciCOMRESET( PAHCI_CHANNEL_EXTENSION ChannelExtension, PAHCI_PORT Px ) /* PHY Reset:COMRESET SCTL.DET Controls the HBA’s device detection and interface initialization. DET=1 Performs interface communication initialization sequence to establish communication. This is functionally equivalent to a hard reset and results in the interface being reset and communications reinitialized. While this field is 1h, COMRESET is transmitted on the interface. Software should leave the DET field set to 1h for a minimum of 1 millisecond to ensure that a COMRESET is sent on the interface. //TODO: Ask Amber, since we are in 5.3.2.3 P:NotRunning and PxCMD.SUD = ‘0’ does this still take us to P:StartComm? Called By: AhciHwReset,AhciNonQueuedErrorRecovery,P_Running_WaitWhileDET1,AhciHwControlIdeStart It assumes: nothing It performs: 5.3.2.11 P:StartComm (overview) 1 Prepare for COMRSET 2 Perform COMRESET 3 Clean up after COMRESET (details) 1.1 make sure ST is 0. DET cannot be altered while ST == 1 as per AHCI 1.1 section 5.3.2.3. 1.2 Don't allow a comm init to trigger a hotplug 1.3 Ignore Hotplug events until the channel is started again 2.1 Perform COMRESET 3.1 Clear SERR Affected Variables,Registers: CMD.ST, SCTL.DET, CI, SACT, SERR Return Values: none */ { AHCI_SERIAL_ATA_CONTROL sctl; AHCI_COMMAND cmd; AHCI_INTERRUPT_ENABLE ieOrig; AHCI_INTERRUPT_ENABLE ieTemp; #ifdef ENABLE_HISTORY_LOG if (ChannelExtension) { RecordExecutionHistory(ChannelExtension, 0x00000010);//AhciCOMRESET } #endif //1.1 make sure ST is 0. DET cannot be altered while ST == 1 as per AHCI 1.1 section 5.3.2.3. cmd.AsUlong = AtaPortReadRegisterUlong(&Px->CMD.AsUlong); if(cmd.ST == 1) { if (ChannelExtension) { RecordExecutionHistory(ChannelExtension, 0x10fa0010);//ST is 1. Abort } return; } //1.2 Don't allow a comm init to trigger a hotplug ieTemp.AsUlong = ieOrig.AsUlong = AtaPortReadRegisterUlong(&Px->IE.AsUlong); ieTemp.PRCE = 0; ieTemp.PCE = 0; AtaPortWriteRegisterUlong(&Px->IE.AsUlong, ieTemp.AsUlong); //1.3 Ignore Hotplug events until the channel is started again ChannelExtension->StateFlags.IgnoreHotplugDueToResetInProgress = TRUE; //2.1 Perform COMRESET sctl.AsUlong = AtaPortReadRegisterUlong(&Px->SCTL.AsUlong); sctl.DET = 1; AtaPortWriteRegisterUlong(&Px->SCTL.AsUlong, sctl.AsUlong); //DET=1 Performs interface communication initialization sequence to establish communication. This is functionally equivalent to a hard reset and results in the interface being reset and communications reinitialized. While this field is 1h, COMRESET is transmitted on the interface. //Software should leave the DET field set to 1h for a minimum of 1 millisecond to ensure that a COMRESET is sent on the interface. AtaPortStallExecution(1000); sctl.AsUlong = AtaPortReadRegisterUlong(&Px->SCTL.AsUlong); sctl.DET = 0; AtaPortWriteRegisterUlong(&Px->SCTL.AsUlong, sctl.AsUlong); //1.2 Don't allow a comm init to trigger a hotplug AtaPortStallExecution(50); //Enable Hotplug again if it was enabled before AtaPortWriteRegisterUlong(&Px->IE.AsUlong, ieOrig.AsUlong); //3.1 Clear SERR AtaPortWriteRegisterUlong(&Px->SERR.AsUlong, (ULONG)~0); RecordExecutionHistory(ChannelExtension, 0x10000010);//Exit AhciCOMRESET }
VOID P_Running_WaitOnBSYDRQ( PAHCI_CHANNEL_EXTENSION ChannelExtension ) /* Home stretch Called By: P_Running It assumes: DET == 3 or IPM == 02 or 06 It performs: (overview) 1 Enable BSY and DRQ to go to 0 2 Wait for BSY and DRQ to go to 0 3 Set ST to 1 (details) 1.1 Clear serr.DIAG.X 2.1 Wait for the rest of the 10 seconds for BSY and DRQ to clear 2.2 After a total amount of 3 seconds working on the start COMRESET 2.3 After waiting for the rest of the 4 seconds for BSY and DRQ to clear, give up 3.1 Set ST to 1 Affected Variables/Registers: */ { AHCI_COMMAND cmd; AHCI_TASK_FILE_DATA tfd; AHCI_SERIAL_ATA_ERROR serr; USHORT totalstarttime; WaitOnBSYDRQ_Start: RecordExecutionHistory(ChannelExtension, 0x0000001a);//P_Running_WaitOnBSYDRQ tfd.AsUlong = AtaPortReadRegisterUlong(&ChannelExtension->Px->TFD.AsUlong); //1.1 Enable BSY and DRQ to go to 0 if ( (tfd.STS.BSY) || (tfd.STS.DRQ) ) { //When [serr.DIAG.X is] set to one this bit indicates a COMINIT signal was received. This bit is reflected in the P0IS.PCS bit. //to allow the TFD to be updated serr.DIAG.X must be cleared. serr.AsUlong = 0; serr.DIAG.X = 1; AtaPortWriteRegisterUlong(&ChannelExtension->Px->SERR.AsUlong, serr.AsUlong); } //3.1 Set ST to 1 if ( ( tfd.STS.BSY == 0) && ( tfd.STS.DRQ == 0) ) { //We made it! Set ST and start the IO we have collected! cmd.AsUlong = AtaPortReadRegisterUlong(&ChannelExtension->Px->CMD.AsUlong); cmd.ST = 1; AtaPortWriteRegisterUlong(&ChannelExtension->Px->CMD.AsUlong, cmd.AsUlong); ChannelExtension->StartState.ChannelNextStartState = StartComplete; ChannelExtension->StateFlags.IgnoreHotplugDueToResetInProgress = FALSE; ActivateQueue(ChannelExtension); RecordExecutionHistory(ChannelExtension, 0x100a001a);//Exit P_Running_WaitOnBSYDRQ Succeeded return; } else { //2.3 After waiting for the remainder of the 10 second maximum Channel Start time for BSY and DRQ to clear, give up totalstarttime = ChannelExtension->StartState.ChannelStateDETCount + ChannelExtension->StartState.ChannelStateDET1Count + ChannelExtension->StartState.ChannelStateDET3Count + ChannelExtension->StartState.ChannelStateFRECount + (ChannelExtension->StartState.ChannelStateBSYDRQCount * 2); if ( totalstarttime > 1000) { P_Running_StartFailed(ChannelExtension); RecordExecutionHistory(ChannelExtension, 0x00ff001a);//P_Running_WaitOnBSYDRQ timed out return; //2.2 After a total amount of 3 seconds working on the start COMRESET } else if ( ChannelExtension->StartState.ChannelStateBSYDRQCount == 50 ){ //Stop FRE,FR in preperation for RESET if (! P_NotRunning(ChannelExtension, ChannelExtension->Px, TRUE) ){ //It takes 1/2 second for stop to fail. //This is taking way too long and the controller is not responding properly. Abort the start. P_Running_StartFailed(ChannelExtension); RecordExecutionHistory(ChannelExtension, 0x00fc001a);//P_Running_WaitOnBSYDRQ Stop Failed on COMRESET return; } //All set, bring down the hammer AhciCOMRESET(ChannelExtension, ChannelExtension->Px); //Set the timers for time remaining. This is a best case scenario and the best that can be offered. ChannelExtension->StartState.ChannelStateDETCount = 0; ChannelExtension->StartState.ChannelStateDET1Count = 0; ChannelExtension->StartState.ChannelStateDET3Count = 0; // won't exceed 100 * 10ms for 100 ms ChannelExtension->StartState.ChannelStateFRECount = 0; // won't exceed 5 * 10ms for 50 ms ChannelExtension->StartState.ChannelStateBSYDRQCount = 51; //won't exceed 500 * 20ms for 10 seconds minus what DET3 and FRE use. //Go back to WaitOnDet3 ChannelExtension->StartState.ChannelNextStartState = WaitOnDET3; RecordExecutionHistory(ChannelExtension, 0x00fd001a);//P_Running_WaitOnBSYDRQ crossing 1 second. COMRESET done, going to WaitOnDET3 P_Running_WaitOnDET3(ChannelExtension); return; //2.1 Wait for the rest of the 4 seconds for BSY and DRQ to clear } else { ChannelExtension->StartState.ChannelStateBSYDRQCount++; RecordExecutionHistory(ChannelExtension, 0x0008001a);//P_Running_WaitOnBSYDRQ still waiting if (ChannelExtension->StateFlags.Crashing) { AtaPortStallExecution(20000); //20 milliseconds goto WaitOnBSYDRQ_Start; } else { if(! AtaPortRequestTimer(ChannelExtension, P_Running, 20000)){ //20 milliseconds AtaPortStallExecution(20000); //20 milliseconds goto WaitOnBSYDRQ_Start; } } return; } } }
VOID ReleaseSlottedCommand( _In_ PAHCI_CHANNEL_EXTENSION ChannelExtension, _In_ UCHAR SlotNumber, _In_ BOOLEAN AtDIRQL ) /*++ Performs Completion Data marshalling back into the SRB It assumes: SRB STATUS has already been filled out ATA status from the AHCI registers is valid for this command SlotNumber points to a fully filled out Slot entry Called by: AhciHwStartIo AhciNonQueuedErrorRecovery AhciCompleteIssuedSRBs It performs: (overview) 1 Intialize 2 Marshal completion data 3 Complete the Command (details) 1.1 Keep track of the completion for our debugging records 2.1 Handle Request Sense marshalling 2.2.1 Handle everything else's marshalling 2.2.2 If this was marked as needing return data, fill in the return Task File 3.1 Make the slot available again 3.2 Perform the Slot's last request, if there was one 3.3.1 IO is completing, that IO may have paused the queue so unpause the queue 3.3.2 Complete the command Affected Variables/Registers: SRB --*/ { PSLOT_CONTENT slotContent; PAHCI_SRB_EXTENSION srbExtension; PSTORAGE_REQUEST_BLOCK srbToRelease; PATA_TASK_FILE returnTaskFile; BOOLEAN isSenseSrb; BOOLEAN retrySrb = FALSE; slotContent = &ChannelExtension->Slot[SlotNumber]; if (slotContent->Srb == NULL) { return; } srbExtension = GetSrbExtension(slotContent->Srb); isSenseSrb = IsRequestSenseSrb(srbExtension->AtaFunction); if (LogExecuteFullDetail(ChannelExtension->AdapterExtension->LogFlags)) { RecordExecutionHistory(ChannelExtension, 0x10000053);//ReleaseSlottedCommand } //2/2 Log command completion part for our debugging records if (LogCommand(ChannelExtension->AdapterExtension->LogFlags)) { PCOMMAND_HISTORY cmdHistory; slotContent->CommandHistoryIndex %= 64; // should not be 64 or bigger. do it anyway to make sure we are safe cmdHistory = &ChannelExtension->CommandHistory[slotContent->CommandHistoryIndex]; StorPortCopyMemory((PVOID)&cmdHistory->CompletionFIS, (PVOID)&ChannelExtension->ReceivedFIS->D2hRegisterFis, sizeof(AHCI_D2H_REGISTER_FIS)); StorPortCopyMemory((PVOID)&cmdHistory->CompletionPx, (PVOID)ChannelExtension->Px, 0x40); cmdHistory->SrbStatus = slotContent->Srb->SrbStatus; } //2. Then complete the command if (isSenseSrb) { //2.1 Handle Request Sense marshalling srbToRelease = (PSTORAGE_REQUEST_BLOCK)SrbGetOriginalRequest(slotContent->Srb); //that original SRB must have SRB_STATUS_AUTOSENSE_VALID set appropriately if (slotContent->Srb->SrbStatus == SRB_STATUS_SUCCESS) { srbToRelease->SrbStatus |= SRB_STATUS_AUTOSENSE_VALID; SrbSetScsiStatus(srbToRelease, SCSISTAT_CHECK_CONDITION); } else { srbToRelease->SrbStatus &= ~SRB_STATUS_AUTOSENSE_VALID; } //finish use of Sense.Srb srbExtension->AtaFunction = 0; } else { //2.2.1 Handle everything else's marshalling srbToRelease = slotContent->Srb; //Record error and status srbExtension->AtaStatus = ChannelExtension->TaskFileData.STS.AsUchar; srbExtension->AtaError = ChannelExtension->TaskFileData.ERR; //2.2.2 If this was marked as needing return data, fill in the return Task File. if( IsReturnResults(srbExtension->Flags) ) { PCDB cdb = SrbGetCdb(srbToRelease); if ((cdb != NULL) && (cdb->CDB10.OperationCode == SCSIOP_ATA_PASSTHROUGH16)) { // for ATA PASS THROUGH 16 command, return Descriptor Format Sense Data, including ATA Status Return info. if (srbExtension->ResultBufferLength >= sizeof(DESCRIPTOR_SENSE_DATA)) { 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; } } else { NT_ASSERT(FALSE); } } else { returnTaskFile = (PATA_TASK_FILE)srbExtension->ResultBuffer; returnTaskFile->Current.bCommandReg = ChannelExtension->ReceivedFIS->D2hRegisterFis.Status; returnTaskFile->Current.bFeaturesReg = ChannelExtension->ReceivedFIS->D2hRegisterFis.Error; returnTaskFile->Current.bCylHighReg = ChannelExtension->ReceivedFIS->D2hRegisterFis.CylHigh; returnTaskFile->Current.bCylLowReg = ChannelExtension->ReceivedFIS->D2hRegisterFis.CylLow; returnTaskFile->Current.bDriveHeadReg = ChannelExtension->ReceivedFIS->D2hRegisterFis.Dev_Head; returnTaskFile->Current.bSectorCountReg = ChannelExtension->ReceivedFIS->D2hRegisterFis.SectorCount; returnTaskFile->Current.bSectorNumberReg = ChannelExtension->ReceivedFIS->D2hRegisterFis.SectorNumber; //if 48bit, get all of it if(Is48BitCommand(srbExtension->Flags) && (srbExtension->ResultBufferLength >= sizeof(ATA_TASK_FILE))) { returnTaskFile->Previous.bCommandReg = ChannelExtension->ReceivedFIS->D2hRegisterFis.Status; returnTaskFile->Previous.bFeaturesReg = ChannelExtension->ReceivedFIS->D2hRegisterFis.Error; returnTaskFile->Previous.bCylHighReg = ChannelExtension->ReceivedFIS->D2hRegisterFis.CylHigh_Exp; returnTaskFile->Previous.bCylLowReg = ChannelExtension->ReceivedFIS->D2hRegisterFis.CylLow_Exp; returnTaskFile->Previous.bDriveHeadReg = ChannelExtension->ReceivedFIS->D2hRegisterFis.Dev_Head; returnTaskFile->Previous.bSectorCountReg = ChannelExtension->ReceivedFIS->D2hRegisterFis.SectorCount_Exp; returnTaskFile->Previous.bSectorNumberReg = ChannelExtension->ReceivedFIS->D2hRegisterFis.SectorNum_Exp; } } // set flag SRB_STATUS_AUTOSENSE_VALID so that Storport will copy it back to original Sense Buffer srbToRelease->SrbStatus |= SRB_STATUS_AUTOSENSE_VALID; } // interpret ATA error to be SCSI error and fill SenseBuffer // also log IO Record if (IsAtaCommand(srbExtension->AtaFunction) || IsAtaCfisPayload(srbExtension->AtaFunction)) { // IO Record for ATA device is logged in AtaMapError() AtaMapError(ChannelExtension, srbToRelease, slotContent->StateFlags.FUA); } else { if (SRB_STATUS(srbToRelease->SrbStatus) == SRB_STATUS_SUCCESS) { ChannelExtension->DeviceExtension[0].IoRecord.SuccessCount++; } else { ChannelExtension->DeviceExtension[0].IoRecord.OtherErrorCount++; } } } //3.1 Make the slot available again slotContent->CmdHeader = NULL; slotContent->CommandHistoryIndex = 0; slotContent->Srb = NULL; slotContent->StateFlags.FUA = FALSE; //Clear the CommandsToComplete bit ChannelExtension->SlotManager.CommandsToComplete &= ~(1 << SlotNumber); ChannelExtension->SlotManager.HighPriorityAttribute &= ~(1 << SlotNumber); //3.3.1 IO is completing, that IO may have paused the queue so unpause the queue ChannelExtension->StateFlags.QueuePaused = FALSE; //3.3.2 Complete the command if ( (srbToRelease->SrbStatus != SRB_STATUS_SUCCESS) && !isSenseSrb && (srbExtension->RetryCount == 0) ) { if ( (ChannelExtension->StateFlags.HybridInfoEnabledOnHiberFile == 1) && IsNCQWriteCommand(srbExtension) ) { // NCQ WRITE with Hybrid Information failed, retry once without Hybrid Information. ChannelExtension->StateFlags.HybridInfoEnabledOnHiberFile = 0; retrySrb = TRUE; } else if (srbToRelease->SrbStatus == SRB_STATUS_PARITY_ERROR) { // if a command encounters CRC error, retry once retrySrb = TRUE; } } if (retrySrb) { srbToRelease->SrbStatus = SRB_STATUS_PENDING; srbExtension->RetryCount++; AhciProcessIo(ChannelExtension, srbToRelease, AtDIRQL); } else { // otherwise, complete it. AhciCompleteRequest(ChannelExtension, srbToRelease, AtDIRQL); } }