一、代码路径以及断点
在winddk的例子里面有msahci代码, 编译成功之后, 替换系统的msahci.sys.重启下断点进行分析.
简单的看下代码在DriverEntry中调用IDE_CONTROLLER_INTERFACE结构,并且调用AtaPortInitializeEx向PCIIDEX创建controller的FDO, 其中包含了AhciChannelInitRoutine、AhciChannelEnabled、AhciAdapterControl函数。对这几个函数下断点,查看调用来源,并且分析函数的作用.
ULONG //REMOVE: 1PCIIDEX to driver entry DriverEntry ( // DriverEntry( PVOID Argument1,// IN PDRIVER_OBJECT DriverObject, PVOID Argument2 // IN PUNICODE_STRING RegistryPath ) // ) /*++ The only goal of DriverEntry is to create an IDE_CONTROLLER_INTERFACE, populate and send it to AtaPortInitializeEx. It assumes: DriverEntry is the first function called in msahci.sys The OS-specific port driver allocates a controller extension initializes it and sends it to AtaPortInitializeEx. AtaPortInitializeEx initializes the miniport driver抯 dispatch tables and allocates an extension for the driver object. It stores the pointer to the AtaControllerInitialize entry point and the ControllerExtensionSize in the driver object extension for later use. Called by: external It performs: (overview) 1. Initialization of the Ide Controller Interface 2. Calling into PCIIDEX through AtaPortInitializeEx. (details) 1.1 Initialize IDE_CONTROLLER_INTERFACE to 0s 1.2 The miniport driver indicates the support for the channel interface by setting the ChannelExtensionSize and the AtaChannelInitRoutine entry point in the controller interface structure. 1.3 Add mini driver entry points 1.4 add pointers to data structures and IO alignment 2.1 The miniport must call AtaPortInitializeEx from its DriverEntry routine. Affected Variables/Registers: none Return Values: The miniport driver must return TRUE if the initialization succeeded. If the miniport driver fails to initialize it must return FALSE. Initialization success is dependant on AtaPortInitializeEx's Return Value --*/ { //used to initialize the IDE Controller Interface IDE_CONTROLLER_INTERFACE IdeControllerInterface; PUCHAR pIdeControllerInterface = (PUCHAR) &IdeControllerInterface; int i=0; //1.1 initialize IDE_CONTROLLER_INTERFACE to 0s for (i= 0; i<sizeof(IDE_CONTROLLER_INTERFACE); i++) { (*(pIdeControllerInterface))=0; pIdeControllerInterface++; } //1.2 The miniport driver indicates the support IdeControllerInterface.ChannelExtensionSize= sizeof(AHCI_CHANNEL_EXTENSION); IdeControllerInterface.AtaChannelInitRoutine= AhciChannelInitRoutine; //1.3 Add mini driver entry points IdeControllerInterface.AtaControllerChannelEnabled = AhciChannelEnabled; IdeControllerInterface.AtaControllerTransferModeSelect = NULL; IdeControllerInterface.AtaAdapterControl= AhciAdapterControl; //1.4 add pointers to data structures and IO alignment IdeControllerInterface.Version= sizeof(IDE_CONTROLLER_INTERFACE); IdeControllerInterface.ControllerExtensionSize= sizeof(AHCI_CONTROLLER_EXTENSION); IdeControllerInterface.AlignmentMask = 1; // The PRDT DBA must be word aligned. //2.1 call into PCIIDEX where it will handle creating the controller's FDO return AtaPortInitializeEx( Argument1, //DriverObject, Argument2, //RegistryPath, &IdeControllerInterface); }
二、调试分析
在msahci!AhciAdapterControl断下来之后, 可以看到调用来源是PCIIDEX!CallAdapterControl函数
我们再看下msahci!AhciAdapterControl函数的实现,该函数是一个中转函数, 处理control的Start、Stop、PowerUp、PowerDown事件。
当前堆栈看到ControlAction为0, 是IdeStart事件, 之后再次中断下来看到的是IdePowerUp事件.
BOOLEAN AhciAdapterControl( PVOID ControllerExtension, IN IDE_CONTROL_ACTION ControlAction, IN PVOID Parameters //when ControlAction is IdeStart, this is a pointer to a IDE_CONTROLLER_CONFIGURATION ) /*++ This is a CONTROLLER dispatch routine for for notifications the miniport driver receives about the various PNP and power events in the system. This function is a function dispatcher for the IDE_CONTROL_ACTIONS: Start, Stop, PowerUp, and PowerDown It assumes: The port driver ensures that there is no outstanding I/O on the adapter before invoking this routine. Called by: external It performs: Dispatch based on the IDE_CONTROL_ACTIONS for the Controller Affected Variables/Registers: none Return Values: The miniport driver must return TRUE to acknowledge the completion of the requested action. A return value of FALSE indicates that the miniport was not able to complete the action successfully. A return value of FALSE for certain actions might cause the device installation to fail. FALSE is only valid if the IDE_CONTROLLER_CONFIGURATION structure is not the right version. This causes the PCIIDEX driver to return from IRP_MJ_PnP with a STATUS_REVISION_MISMATCH --*/ { //Used to perform dispatch BOOLEAN retVal; PIDE_CONTROLLER_CONFIGURATION controllerConfiguration; switch (ControlAction) { case IdeStart: controllerConfiguration = Parameters; retVal = AhciAdapterControlIdeStart(ControllerExtension, controllerConfiguration); break; case IdeStop: retVal = AhciAdapterControlIdeStop(ControllerExtension); break; case IdePowerUp: retVal = AhciAdapterControlIdePowerUp(ControllerExtension); break; case IdePowerDown: retVal = AhciAdapterControlIdePowerDown(ControllerExtension); break; default: retVal = FALSE; break; } return retVal; }
在msahci!AhciChannelEnabled中断下来后, 看到堆栈情况如下:
我们看下这个函数的代码实现,这个函数是给每个通道创建PDO. 一旦PDO创建, 这时就会加载ataport去开启通道并且在每个通道上枚举设备。
ATA_CHANNEL_STATE AhciChannelEnabled( IN PVOID ControllerExtension, IN ULONG Channel ) /*++ AtaControllerChannelEnabled is an optional routine. This is an optional routine and should not have controller critical steps in it. This function is not called in the case of a channel restart, only on QueryDeviceRelations If the miniport driver does not implement this routine a default handler will be loaded, and all channels are assumed to be enabled. PCIIDEX uses this function to help it build PDOs for each channel. Once the PDOs are created, it is time to load ATAport who will start the channels and do enumeration for the device(s) on the channel(s). It assumes: This function will be called with 'ULONG Channel' valuse from 0 to (ControllerConfiguration->NumberOfChannels - 1) which was given to PCIIDEX in AhciAdapterControl (IdeStart). Called by: external It performs: (overview) 1. Verification of channel's existance (details) 1.1 Initialize variables 1.2 Verify the channel's existance Ataport doesn't support sparse channels explicitly. It is possible this channel doesn't exist. Affected Variables/Registers: Return Values: ChannelStateEnabled = Channel is ready to be used by time this function finishes ChannelStateDisabled= If previously enabled, this state causes the port driver to mark the channel dead ChannelStateUnKnown = use only if you want the port driver to assert. --*/ { ATA_CHANNEL_STATE state; PAHCI_CONTROLLER_EXTENSION controllerExtension; //structures to get information from the Controller PAHCI_MEMORY_REGISTERS abar; ULONG pi; //this is an optional routine and should not have controller critical steps in it. //this is not called in the case of a channel restart, only on QueryDeviceRelations //1.1 Initialize variables state = ChannelStateUnKnown; controllerExtension = (PAHCI_CONTROLLER_EXTENSION) ControllerExtension; abar = (PAHCI_MEMORY_REGISTERS) controllerExtension->ABAR_Address; pi = AtaPortReadRegisterUlong(&abar->PI); //1.2 Verify the channel's existance if ( (pi & (1 << Channel) ) == 0){ return ChannelStateDisabled; } return ChannelStateEnabled; }
以上初始化步骤完成之后,ataport就会调用msahci!AhciChannelInitRoutine对通道上的每个设备进行初始化。
我们来看看这个函数的代码实现,msahci!AhciChannelInitRoutine就将ataport初始化一部分的Channel interface进行初始化.该函数是在ataport的AddDevice创建通道的FDO扩展时调用.
BOOLEAN AhciChannelInitRoutine( IN PVOID ChannelExtension, OUT PIDE_CHANNEL_INTERFACE ChannelInterface, IN PVOID ControllerExtension ) { /*++ This is the first time that we see the Channel Interface. Ataport has initialized some of it, we have to initialize the rest. This is the first time that we see the Channel Extension whose size was specified in IdeControllerInterface.ChannelExtensionSize at DriverEntry. Time to start initializing the Channel Extension. It assumes: The ChannelInitialize routine is called when Ataport's AddDevice is creating the Channel's FDO Extension. Called by: external It performs: (overview) 1. Start with some defensive structure checking 2. Fill in ControllerExtension 3. Initialization of the Channel Extension 4. Initialization of the Channel Interface (details) 1.1 Initialize Variables 1.2 Verify the Channel Interface is the version we are expecting. 2.1 Fill in ControllerExtension 3.1 Initialize Channel Extension 3.2 Check for controller matched accomodations 3.3 Keep a copy of the Controller's Capabilities 3.4 Finally, Intialize the Command and Interrupt logging 4.1 Initialization of the Channel Interface Affected Variables/Registers: ControllerExtension ChannelExtension ChannelInterface Return Values: FALSE is only valid if the IDE_CONTROLLER_CONFIGURATION structure is not the right version. This causes the AtaPort driver to return from IRP_MJ_PnP, IRP_MN_START with a STATUS_REVISION_MISMATCH True if the function excecuted entirely. */ PAHCI_CHANNEL_EXTENSION channelExtension; PAHCI_CONTROLLER_EXTENSION controllerExtension; //1.1 Initialize Variables channelExtension = (PAHCI_CHANNEL_EXTENSION) ChannelExtension; controllerExtension = (PAHCI_CONTROLLER_EXTENSION) ControllerExtension; #ifdef ENABLE_HISTORY_LOG RecordExecutionHistory(channelExtension, 0x00000001);//AhciChannelInitRoutine #endif //1.2 Verify the Channel Interface if (ChannelInterface->Version != sizeof(IDE_CHANNEL_INTERFACE) ){ RecordExecutionHistory(channelExtension, 0x10ff0001);//AhciChannelInitRoutine failed. Structure version mismatch. return FALSE; } //2.1 Fill in ControllerExtension controllerExtension->ExtensionOnSlot[ChannelInterface->ChannelNumber] = channelExtension; channelExtension->ControllerExtension = (PVOID) controllerExtension; //3.1 Initialize Channel Extension channelExtension->ChannelNumber = ChannelInterface->ChannelNumber; channelExtension->StateFlags.StartCapable = FALSE; channelExtension->StateFlags.Crashing = FALSE; channelExtension->StateFlags.IgnoreHotplugDueToResetInProgress = TRUE; channelExtension->StateFlags.QueuePaused = FALSE; channelExtension->StateFlags.NCQ_Disallowed = FALSE; channelExtension->StateFlags.NCQ_Activated = FALSE; channelExtension->StateFlags.NCQ_Succeeded = FALSE; channelExtension->StateFlags.NCQ_NeverNonQueuedErrorRecovery = FALSE; channelExtension->StateFlags.CallAhciReset = FALSE; channelExtension->StateFlags.CallAhciNonQueuedErrorRecovery = FALSE; channelExtension->StateFlags.CallAhciReportBusChange = FALSE; channelExtension->StateFlags.IgnoreHotPlug = FALSE; channelExtension->StateFlags.SingleIo = FALSE; channelExtension->StateFlags.ResetInInit = FALSE; channelExtension->StateFlags.CallAhciAutoPartialToSlumber = FALSE; channelExtension->StateFlags.AN_Enabled = FALSE; channelExtension->SendGesnToDevice = FALSE; channelExtension->StateFlags.CLOReset_Enable = FALSE; //3.2 Check for controller matched accomodations channelExtension->SlotManager.CommandsIssued = 0; channelExtension->SlotManager.CommandsToComplete = 0; channelExtension->SlotManager.HighPriorityAttribute = 0; channelExtension->SlotManager.NCQueueSlice = 0; channelExtension->SlotManager.NormalQueueSlice = 0; channelExtension->SlotManager.SingleIoSlice = 0; channelExtension->CCS = 1; //3.3 Keep a copy of the Controller's Capabilities channelExtension->CAP = controllerExtension->CAP; channelExtension->CAP2 = controllerExtension->CAP2; //3.4 Finally, Intialize the Command and Interrupt logging channelExtension->CommandHistoryNextAvailableIndex = 0; channelExtension->ExecutionHistoryNextAvailableIndex = 0; //4.1 Initialization of the Channel Interface ChannelInterface->IdeHwInitialize = AhciHwInitialize; ChannelInterface->IdeHwBuildIo = AhciHwBuildIo; ChannelInterface->IdeHwStartIo = AhciHwStartIo; ChannelInterface->IdeHwInterrupt = AhciHwInterrupt; ChannelInterface->IdeHwReset = AhciHwReset; ChannelInterface->IdeHwControl = AhciHwControl; RecordExecutionHistory(channelExtension, 0x10000001);//Exit AhciChannelInitRoutine return TRUE; }