/* GLOBALS ********************************************************************/
+PIO_RESOURCE_REQUIREMENTS_LIST PciZeroIoResourceRequirements;
+
+PCI_CONFIGURATOR PciConfigurators[] =
+{
+ {
+ Device_MassageHeaderForLimitsDetermination,
+ Device_RestoreCurrent,
+ Device_SaveLimits,
+ Device_SaveCurrentSettings,
+ Device_ChangeResourceSettings,
+ Device_GetAdditionalResourceDescriptors,
+ Device_ResetDevice
+ },
+ {
+ PPBridge_MassageHeaderForLimitsDetermination,
+ PPBridge_RestoreCurrent,
+ PPBridge_SaveLimits,
+ PPBridge_SaveCurrentSettings,
+ PPBridge_ChangeResourceSettings,
+ PPBridge_GetAdditionalResourceDescriptors,
+ PPBridge_ResetDevice
+ },
+ {
+ Cardbus_MassageHeaderForLimitsDetermination,
+ Cardbus_RestoreCurrent,
+ Cardbus_SaveLimits,
+ Cardbus_SaveCurrentSettings,
+ Cardbus_ChangeResourceSettings,
+ Cardbus_GetAdditionalResourceDescriptors,
+ Cardbus_ResetDevice
+ }
+};
+
/* FUNCTIONS ******************************************************************/
+BOOLEAN
+NTAPI
+PciComputeNewCurrentSettings(IN PPCI_PDO_EXTENSION PdoExtension,
+ IN PCM_RESOURCE_LIST ResourceList)
+{
+ PCM_PARTIAL_RESOURCE_DESCRIPTOR Partial, InterruptResource;
+ PCM_PARTIAL_RESOURCE_DESCRIPTOR BaseResource, CurrentDescriptor;
+ PCM_PARTIAL_RESOURCE_DESCRIPTOR PreviousDescriptor;
+ CM_PARTIAL_RESOURCE_DESCRIPTOR ResourceArray[7];
+ PCM_FULL_RESOURCE_DESCRIPTOR FullList;
+ BOOLEAN DrainPartial, RangeChange;
+ ULONG i, j;
+ PPCI_FUNCTION_RESOURCES PciResources;
+ PAGED_CODE();
+
+ /* Make sure we have either no resources, or at least one */
+ ASSERT((ResourceList == NULL) || (ResourceList->Count == 1));
+
+ /* Initialize no partial, interrupt descriptor, or range change */
+ Partial = NULL;
+ InterruptResource = NULL;
+ RangeChange = FALSE;
+
+ /* Check if there's not actually any resources */
+ if (!(ResourceList) || !(ResourceList->Count))
+ {
+ /* Then just return the hardware update state */
+ return PdoExtension->UpdateHardware;
+ }
+
+ /* Print the new specified resource list */
+ PciDebugPrintCmResList(ResourceList);
+
+ /* Clear the temporary resource array */
+ for (i = 0; i < 7; i++) ResourceArray[i].Type = CmResourceTypeNull;
+
+ /* Loop the full resource descriptor */
+ FullList = ResourceList->List;
+ for (i = 0; i < ResourceList->Count; i++)
+ {
+ /* Initialize loop variables */
+ DrainPartial = FALSE;
+ BaseResource = NULL;
+
+ /* Loop the partial descriptors */
+ Partial = FullList->PartialResourceList.PartialDescriptors;
+ for (j = 0; j < FullList->PartialResourceList.Count; j++)
+ {
+ /* Check if we were supposed to drain a partial due to device data */
+ if (DrainPartial)
+ {
+ /* Draining complete, move on to the next descriptor then */
+ DrainPartial--;
+ continue;
+ }
+
+ /* Check what kind of descriptor this was */
+ switch (Partial->Type)
+ {
+ /* Base BAR resources */
+ case CmResourceTypePort:
+ case CmResourceTypeMemory:
+
+ /* Set it as the base */
+ ASSERT(BaseResource == NULL);
+ BaseResource = Partial;
+ break;
+
+ /* Interrupt resource */
+ case CmResourceTypeInterrupt:
+
+ /* Make sure it's a compatible (and the only) PCI interrupt */
+ ASSERT(InterruptResource == NULL);
+ ASSERT(Partial->u.Interrupt.Level == Partial->u.Interrupt.Vector);
+ InterruptResource = Partial;
+
+ /* Only 255 interrupts on x86/x64 hardware */
+ if (Partial->u.Interrupt.Level < 256)
+ {
+ /* Use the passed interrupt line */
+ PdoExtension->AdjustedInterruptLine = Partial->u.Interrupt.Level;
+ }
+ else
+ {
+ /* Invalid vector, so ignore it */
+ PdoExtension->AdjustedInterruptLine = 0;
+ }
+
+ break;
+
+ /* Check for specific device data */
+ case CmResourceTypeDevicePrivate:
+
+ /* Check what kind of data this was */
+ switch (Partial->u.DevicePrivate.Data[0])
+ {
+ /* Not used in the driver yet */
+ case 1:
+ UNIMPLEMENTED_DBGBREAK();
+ break;
+
+ /* Not used in the driver yet */
+ case 2:
+ UNIMPLEMENTED_DBGBREAK();
+ break;
+
+ /* A drain request */
+ case 3:
+ /* Shouldn't be a base resource, this is a drain */
+ ASSERT(BaseResource == NULL);
+ DrainPartial = Partial->u.DevicePrivate.Data[1];
+ ASSERT(DrainPartial == TRUE);
+ break;
+ }
+ break;
+ }
+
+ /* Move to the next descriptor */
+ Partial = PciNextPartialDescriptor(Partial);
+ }
+
+ /* We should be starting a new list now */
+ ASSERT(BaseResource == NULL);
+ FullList = (PVOID)Partial;
+ }
+
+ /* Check the current assigned PCI resources */
+ PciResources = PdoExtension->Resources;
+ if (!PciResources) return FALSE;
+
+ //if... // MISSING CODE
+ UNIMPLEMENTED;
+ DPRINT1("Missing sanity checking code!\n");
+
+ /* Loop all the PCI function resources */
+ for (i = 0; i < 7; i++)
+ {
+ /* Get the current function resource descriptor, and the new one */
+ CurrentDescriptor = &PciResources->Current[i];
+ Partial = &ResourceArray[i];
+
+ /* Previous is current during the first loop iteration */
+ PreviousDescriptor = &PciResources->Current[(i == 0) ? (0) : (i - 1)];
+
+ /* Check if this new descriptor is different than the old one */
+ if (((Partial->Type != CurrentDescriptor->Type) ||
+ (Partial->Type != CmResourceTypeNull)) &&
+ ((Partial->u.Generic.Start.QuadPart !=
+ CurrentDescriptor->u.Generic.Start.QuadPart) ||
+ (Partial->u.Generic.Length != CurrentDescriptor->u.Generic.Length)))
+ {
+ /* Record a change */
+ RangeChange = TRUE;
+
+ /* Was there a range before? */
+ if (CurrentDescriptor->Type != CmResourceTypeNull)
+ {
+ /* Print it */
+ DbgPrint(" Old range-\n");
+ PciDebugPrintPartialResource(CurrentDescriptor);
+ }
+ else
+ {
+ /* There was no range */
+ DbgPrint(" Previously unset range\n");
+ }
+
+ /* Print new one */
+ DbgPrint(" changed to\n");
+ PciDebugPrintPartialResource(Partial);
+
+ /* Update to new range */
+ CurrentDescriptor->Type = Partial->Type;
+ PreviousDescriptor->u.Generic.Start = Partial->u.Generic.Start;
+ PreviousDescriptor->u.Generic.Length = Partial->u.Generic.Length;
+ CurrentDescriptor = PreviousDescriptor;
+ }
+ }
+
+ /* Either the hardware was updated, or a resource range changed */
+ return ((RangeChange) || (PdoExtension->UpdateHardware));
+}
+
+VOID
+NTAPI
+PcipUpdateHardware(IN PVOID Context,
+ IN PVOID Context2)
+{
+ PPCI_PDO_EXTENSION PdoExtension = Context;
+ PPCI_COMMON_HEADER PciData = Context2;
+
+ /* Check if we're allowed to disable decodes */
+ PciData->Command = PdoExtension->CommandEnables;
+ if (!(PdoExtension->HackFlags & PCI_HACK_PRESERVE_COMMAND))
+ {
+ /* Disable all decodes */
+ PciData->Command &= ~(PCI_ENABLE_IO_SPACE |
+ PCI_ENABLE_MEMORY_SPACE |
+ PCI_ENABLE_BUS_MASTER |
+ PCI_ENABLE_WRITE_AND_INVALIDATE);
+ }
+
+ /* Update the device configuration */
+ PciData->Status = 0;
+ PciWriteDeviceConfig(PdoExtension, PciData, 0, PCI_COMMON_HDR_LENGTH);
+
+ /* Turn decodes back on */
+ PciDecodeEnable(PdoExtension, TRUE, &PdoExtension->CommandEnables);
+}
+
+VOID
+NTAPI
+PciUpdateHardware(IN PPCI_PDO_EXTENSION PdoExtension,
+ IN PPCI_COMMON_HEADER PciData)
+{
+ PCI_IPI_CONTEXT Context;
+
+ /* Check for critical devices and PCI Debugging devices */
+ if ((PdoExtension->HackFlags & PCI_HACK_CRITICAL_DEVICE) ||
+ (PdoExtension->OnDebugPath))
+ {
+ /* Build the context and send an IPI */
+ Context.RunCount = 1;
+ Context.Barrier = 1;
+ Context.Context = PciData;
+ Context.Function = PcipUpdateHardware;
+ Context.DeviceExtension = PdoExtension;
+ KeIpiGenericCall(PciExecuteCriticalSystemRoutine, (ULONG_PTR)&Context);
+ }
+ else
+ {
+ /* Just to the update inline */
+ PcipUpdateHardware(PdoExtension, PciData);
+ }
+}
+
+PIO_RESOURCE_REQUIREMENTS_LIST
+NTAPI
+PciAllocateIoRequirementsList(IN ULONG Count,
+ IN ULONG BusNumber,
+ IN ULONG SlotNumber)
+{
+ SIZE_T Size;
+ PIO_RESOURCE_REQUIREMENTS_LIST RequirementsList;
+
+ /* Calculate the final size of the list, including each descriptor */
+ Size = sizeof(IO_RESOURCE_REQUIREMENTS_LIST);
+ if (Count > 1) Size = sizeof(IO_RESOURCE_DESCRIPTOR) * (Count - 1) +
+ sizeof(IO_RESOURCE_REQUIREMENTS_LIST);
+
+ /* Allocate the list */
+ RequirementsList = ExAllocatePoolWithTag(PagedPool, Size, 'BicP');
+ if (!RequirementsList) return NULL;
+
+ /* Initialize it */
+ RtlZeroMemory(RequirementsList, Size);
+ RequirementsList->AlternativeLists = 1;
+ RequirementsList->BusNumber = BusNumber;
+ RequirementsList->SlotNumber = SlotNumber;
+ RequirementsList->InterfaceType = PCIBus;
+ RequirementsList->ListSize = Size;
+ RequirementsList->List[0].Count = Count;
+ RequirementsList->List[0].Version = 1;
+ RequirementsList->List[0].Revision = 1;
+
+ /* Return it */
+ return RequirementsList;
+}
+
+PCM_RESOURCE_LIST
+NTAPI
+PciAllocateCmResourceList(IN ULONG Count,
+ IN ULONG BusNumber)
+{
+ SIZE_T Size;
+ PCM_RESOURCE_LIST ResourceList;
+
+ /* Calculate the final size of the list, including each descriptor */
+ Size = sizeof(CM_RESOURCE_LIST);
+ if (Count > 1) Size = sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR) * (Count - 1) +
+ sizeof(CM_RESOURCE_LIST);
+
+ /* Allocate the list */
+ ResourceList = ExAllocatePoolWithTag(PagedPool, Size, 'BicP');
+ if (!ResourceList) return NULL;
+
+ /* Initialize it */
+ RtlZeroMemory(ResourceList, Size);
+ ResourceList->Count = 1;
+ ResourceList->List[0].BusNumber = BusNumber;
+ ResourceList->List[0].InterfaceType = PCIBus;
+ ResourceList->List[0].PartialResourceList.Version = 1;
+ ResourceList->List[0].PartialResourceList.Revision = 1;
+ ResourceList->List[0].PartialResourceList.Count = Count;
+
+ /* Return it */
+ return ResourceList;
+}
+
+NTSTATUS
+NTAPI
+PciQueryResources(IN PPCI_PDO_EXTENSION PdoExtension,
+ OUT PCM_RESOURCE_LIST *Buffer)
+{
+ PPCI_FUNCTION_RESOURCES PciResources;
+ BOOLEAN HaveVga, HaveMemSpace, HaveIoSpace;
+ USHORT BridgeControl, PciCommand;
+ ULONG Count, i;
+ PCM_PARTIAL_RESOURCE_DESCRIPTOR Partial, Resource, LastResource;
+ PCM_RESOURCE_LIST ResourceList;
+ UCHAR InterruptLine;
+ PAGED_CODE();
+
+ /* Assume failure */
+ Count = 0;
+ HaveVga = FALSE;
+ *Buffer = NULL;
+
+ /* Make sure there's some resources to query */
+ PciResources = PdoExtension->Resources;
+ if (!PciResources) return STATUS_SUCCESS;
+
+ /* Read the decodes */
+ PciReadDeviceConfig(PdoExtension,
+ &PciCommand,
+ FIELD_OFFSET(PCI_COMMON_HEADER, Command),
+ sizeof(USHORT));
+
+ /* Check which ones are turned on */
+ HaveIoSpace = PciCommand & PCI_ENABLE_IO_SPACE;
+ HaveMemSpace = PciCommand & PCI_ENABLE_MEMORY_SPACE;
+
+ /* Loop maximum possible descriptors */
+ for (i = 0; i < 7; i++)
+ {
+ /* Check if the decode for this descriptor is actually turned on */
+ Partial = &PciResources->Current[i];
+ if (((HaveMemSpace) && (Partial->Type == CmResourceTypeMemory)) ||
+ ((HaveIoSpace) && (Partial->Type == CmResourceTypePort)))
+ {
+ /* One more fully active descriptor */
+ Count++;
+ }
+ }
+
+ /* If there's an interrupt pin associated, check at least one decode is on */
+ if ((PdoExtension->InterruptPin) && ((HaveMemSpace) || (HaveIoSpace)))
+ {
+ /* Read the interrupt line for the pin, add a descriptor if it's valid */
+ InterruptLine = PdoExtension->AdjustedInterruptLine;
+ if ((InterruptLine) && (InterruptLine != -1)) Count++;
+ }
+
+ /* Check for PCI bridge */
+ if (PdoExtension->HeaderType == PCI_BRIDGE_TYPE)
+ {
+ /* Read bridge settings, check if VGA is present */
+ PciReadDeviceConfig(PdoExtension,
+ &BridgeControl,
+ FIELD_OFFSET(PCI_COMMON_HEADER, u.type1.BridgeControl),
+ sizeof(USHORT));
+ if (BridgeControl & PCI_ENABLE_BRIDGE_VGA)
+ {
+ /* Remember for later */
+ HaveVga = TRUE;
+
+ /* One memory descriptor for 0xA0000, plus the two I/O port ranges */
+ if (HaveMemSpace) Count++;
+ if (HaveIoSpace) Count += 2;
+ }
+ }
+
+ /* If there's no descriptors in use, there's no resources, so return */
+ if (!Count) return STATUS_SUCCESS;
+
+ /* Allocate a resource list to hold the resources */
+ ResourceList = PciAllocateCmResourceList(Count,
+ PdoExtension->ParentFdoExtension->BaseBus);
+ if (!ResourceList) return STATUS_INSUFFICIENT_RESOURCES;
+
+ /* This is where the descriptors will be copied into */
+ Resource = ResourceList->List[0].PartialResourceList.PartialDescriptors;
+ LastResource = Resource + Count + 1;
+
+ /* Loop maximum possible descriptors */
+ for (i = 0; i < 7; i++)
+ {
+ /* Check if the decode for this descriptor is actually turned on */
+ Partial = &PciResources->Current[i];
+ if (((HaveMemSpace) && (Partial->Type == CmResourceTypeMemory)) ||
+ ((HaveIoSpace) && (Partial->Type == CmResourceTypePort)))
+ {
+ /* Copy the descriptor into the resource list */
+ *Resource++ = *Partial;
+ }
+ }
+
+ /* Check if earlier the code detected this was a PCI bridge with VGA on it */
+ if (HaveVga)
+ {
+ /* Are the memory decodes enabled? */
+ if (HaveMemSpace)
+ {
+ /* Build a memory descriptor for a 128KB framebuffer at 0xA0000 */
+ Resource->Flags = CM_RESOURCE_MEMORY_READ_WRITE;
+ Resource->u.Generic.Start.HighPart = 0;
+ Resource->Type = CmResourceTypeMemory;
+ Resource->u.Generic.Start.LowPart = 0xA0000;
+ Resource->u.Generic.Length = 0x20000;
+ Resource++;
+ }
+
+ /* Are the I/O decodes enabled? */
+ if (HaveIoSpace)
+ {
+ /* Build an I/O descriptor for the graphic ports at 0x3B0 */
+ Resource->Type = CmResourceTypePort;
+ Resource->Flags = CM_RESOURCE_PORT_POSITIVE_DECODE | CM_RESOURCE_PORT_10_BIT_DECODE;
+ Resource->u.Port.Start.QuadPart = 0x3B0u;
+ Resource->u.Port.Length = 0xC;
+ Resource++;
+
+ /* Build an I/O descriptor for the graphic ports at 0x3C0 */
+ Resource->Type = CmResourceTypePort;
+ Resource->Flags = CM_RESOURCE_PORT_POSITIVE_DECODE | CM_RESOURCE_PORT_10_BIT_DECODE;
+ Resource->u.Port.Start.QuadPart = 0x3C0u;
+ Resource->u.Port.Length = 0x20;
+ Resource++;
+ }
+ }
+
+ /* If there's an interrupt pin associated, check at least one decode is on */
+ if ((PdoExtension->InterruptPin) && ((HaveMemSpace) || (HaveIoSpace)))
+ {
+ /* Read the interrupt line for the pin, check if it's valid */
+ InterruptLine = PdoExtension->AdjustedInterruptLine;
+ if ((InterruptLine) && (InterruptLine != -1))
+ {
+ /* Make sure there's still space */
+ ASSERT(Resource < LastResource);
+
+ /* Add the interrupt descriptor */
+ Resource->Flags = CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE;
+ Resource->Type = CmResourceTypeInterrupt;
+ Resource->ShareDisposition = CmResourceShareShared;
+ Resource->u.Interrupt.Affinity = -1;
+ Resource->u.Interrupt.Level = InterruptLine;
+ Resource->u.Interrupt.Vector = InterruptLine;
+ }
+ }
+
+ /* Return the resouce list */
+ *Buffer = ResourceList;
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS
+NTAPI
+PciQueryTargetDeviceRelations(IN PPCI_PDO_EXTENSION PdoExtension,
+ IN OUT PDEVICE_RELATIONS *pDeviceRelations)
+{
+ PDEVICE_RELATIONS DeviceRelations;
+ PAGED_CODE();
+
+ /* If there were existing relations, free them */
+ if (*pDeviceRelations) ExFreePoolWithTag(*pDeviceRelations, 0);
+
+ /* Allocate a new structure for the relations */
+ DeviceRelations = ExAllocatePoolWithTag(NonPagedPool,
+ sizeof(DEVICE_RELATIONS),
+ 'BicP');
+ if (!DeviceRelations) return STATUS_INSUFFICIENT_RESOURCES;
+
+ /* Only one relation: the PDO */
+ DeviceRelations->Count = 1;
+ DeviceRelations->Objects[0] = PdoExtension->PhysicalDeviceObject;
+ ObReferenceObject(DeviceRelations->Objects[0]);
+
+ /* Return the new relations */
+ *pDeviceRelations = DeviceRelations;
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS
+NTAPI
+PciQueryEjectionRelations(IN PPCI_PDO_EXTENSION PdoExtension,
+ IN OUT PDEVICE_RELATIONS *pDeviceRelations)
+{
+ UNREFERENCED_PARAMETER(PdoExtension);
+ UNREFERENCED_PARAMETER(pDeviceRelations);
+
+ /* Not yet implemented */
+ UNIMPLEMENTED_DBGBREAK();
+ return STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS
+NTAPI
+PciBuildRequirementsList(IN PPCI_PDO_EXTENSION PdoExtension,
+ IN PPCI_COMMON_HEADER PciData,
+ OUT PIO_RESOURCE_REQUIREMENTS_LIST* Buffer)
+{
+ PIO_RESOURCE_REQUIREMENTS_LIST RequirementsList;
+
+ UNREFERENCED_PARAMETER(PdoExtension);
+ UNREFERENCED_PARAMETER(PciData);
+
+ {
+ /* There aren't, so use the zero descriptor */
+ RequirementsList = PciZeroIoResourceRequirements;
+
+ /* Does it actually exist yet? */
+ if (!PciZeroIoResourceRequirements)
+ {
+ /* Allocate it, and use it for future use */
+ RequirementsList = PciAllocateIoRequirementsList(0, 0, 0);
+ PciZeroIoResourceRequirements = RequirementsList;
+ if (!PciZeroIoResourceRequirements) return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ /* Return the zero requirements list to the caller */
+ *Buffer = RequirementsList;
+ DPRINT1("PCI - build resource reqs - early out, 0 resources\n");
+ return STATUS_SUCCESS;
+ }
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS
+NTAPI
+PciQueryRequirements(IN PPCI_PDO_EXTENSION PdoExtension,
+ IN OUT PIO_RESOURCE_REQUIREMENTS_LIST *RequirementsList)
+{
+ NTSTATUS Status;
+ PCI_COMMON_HEADER PciHeader;
+ PAGED_CODE();
+
+ /* Check if the PDO has any resources, or at least an interrupt pin */
+ if ((PdoExtension->Resources) || (PdoExtension->InterruptPin))
+ {
+ /* Read the current PCI header */
+ PciReadDeviceConfig(PdoExtension, &PciHeader, 0, PCI_COMMON_HDR_LENGTH);
+
+ /* Use it to build a list of requirements */
+ Status = PciBuildRequirementsList(PdoExtension, &PciHeader, RequirementsList);
+ if (!NT_SUCCESS(Status)) return Status;
+
+ /* Is this a Compaq PCI Hotplug Controller (r17) on a PAE system ? */
+ if ((PciHeader.VendorID == 0xE11) &&
+ (PciHeader.DeviceID == 0xA0F7) &&
+ (PciHeader.RevisionID == 17) &&
+ (ExIsProcessorFeaturePresent(PF_PAE_ENABLED)))
+ {
+ /* Have not tested this on eVb's machine yet */
+ UNIMPLEMENTED_DBGBREAK();
+ }
+
+ /* Check if the requirements are actually the zero list */
+ if (*RequirementsList == PciZeroIoResourceRequirements)
+ {
+ /* A simple NULL will sufficie for the PnP Manager */
+ *RequirementsList = NULL;
+ DPRINT1("Returning NULL requirements list\n");
+ }
+ else
+ {
+ /* Otherwise, print out the requirements list */
+ PciDebugPrintIoResReqList(*RequirementsList);
+ }
+ }
+ else
+ {
+ /* There aren't any resources, so simply return NULL */
+ DPRINT1("PciQueryRequirements returning NULL requirements list\n");
+ *RequirementsList = NULL;
+ }
+
+ /* This call always succeeds (but maybe with no requirements) */
+ return STATUS_SUCCESS;
+}
+
+/*
+ * 7. The IO/MEM/Busmaster decodes are disabled for the device.
+ * 8. The PCI bus driver sets the operating mode bits of the Programming
+ * Interface byte to switch the controller to native mode.
+ *
+ * Important: When the controller is set to native mode, it must quiet itself
+ * and must not decode I/O resources or generate interrupts until the operating
+ * system has enabled the ports in the PCI configuration header.
+ * The IO/MEM/BusMaster bits will be disabled before the mode change, but it
+ * is not possible to disable interrupts on the device. The device must not
+ * generate interrupts (either legacy or native mode) while the decodes are
+ * disabled in the command register.
+ *
+ * This operation is expected to be instantaneous and the operating system does
+ * not stall afterward. It is also expected that the interrupt pin register in
+ * the PCI Configuration space for this device is accurate. The operating system
+ * re-reads this data after previously ignoring it.
+ */
+BOOLEAN
+NTAPI
+PciConfigureIdeController(IN PPCI_PDO_EXTENSION PdoExtension,
+ IN PPCI_COMMON_HEADER PciData,
+ IN BOOLEAN Initial)
+{
+ UCHAR MasterMode, SlaveMode, MasterFixed, SlaveFixed, ProgIf, NewProgIf;
+ BOOLEAN Switched;
+ USHORT Command;
+
+ /* Assume it won't work */
+ Switched = FALSE;
+
+ /* Get master and slave current settings, and programmability flag */
+ ProgIf = PciData->ProgIf;
+ MasterMode = (ProgIf & 1) == 1;
+ MasterFixed = (ProgIf & 2) == 0;
+ SlaveMode = (ProgIf & 4) == 4;
+ SlaveFixed = (ProgIf & 8) == 0;
+
+ /*
+ * [..] In order for Windows XP SP1 and Windows Server 2003 to switch an ATA
+ * ATA controller from compatible mode to native mode, the following must be
+ * true:
+ *
+ * - The controller must indicate in its programming interface that both channels
+ * can be switched to native mode. Windows XP SP1 and Windows Server 2003 do
+ * not support switching only one IDE channel to native mode. See the PCI IDE
+ * Controller Specification Revision 1.0 for details.
+ */
+ if ((MasterMode != SlaveMode) || (MasterFixed != SlaveFixed))
+ {
+ /* Windows does not support this configuration, fail */
+ DPRINT1("PCI: Warning unsupported IDE controller configuration for VEN_%04x&DEV_%04x!",
+ PdoExtension->VendorId,
+ PdoExtension->DeviceId);
+ return Switched;
+ }
+
+ /* Check if the controller is already in native mode */
+ if ((MasterMode) && (SlaveMode))
+ {
+ /* Check if I/O decodes should be disabled */
+ if ((Initial) || (PdoExtension->IoSpaceUnderNativeIdeControl))
+ {
+ /* Read the current command */
+ PciReadDeviceConfig(PdoExtension,
+ &Command,
+ FIELD_OFFSET(PCI_COMMON_HEADER, Command),
+ sizeof(USHORT));
+
+ /* Disable I/O space decode */
+ Command &= ~PCI_ENABLE_IO_SPACE;
+
+ /* Update new command in PCI IDE controller */
+ PciWriteDeviceConfig(PdoExtension,
+ &Command,
+ FIELD_OFFSET(PCI_COMMON_HEADER, Command),
+ sizeof(USHORT));
+
+ /* Save updated command value */
+ PciData->Command = Command;
+ }
+
+ /* The controller is now in native mode */
+ Switched = TRUE;
+ }
+ else if (!(MasterFixed) &&
+ !(SlaveFixed) &&
+ (PdoExtension->BIOSAllowsIDESwitchToNativeMode) &&
+ !(PdoExtension->HackFlags & PCI_HACK_DISABLE_IDE_NATIVE_MODE))
+ {
+ /* Turn off decodes */
+ PciDecodeEnable(PdoExtension, FALSE, NULL);
+
+ /* Update the current command */
+ PciReadDeviceConfig(PdoExtension,
+ &PciData->Command,
+ FIELD_OFFSET(PCI_COMMON_HEADER, Command),
+ sizeof(USHORT));
+
+ /* Enable native mode */
+ ProgIf = PciData->ProgIf | 5;
+ PciWriteDeviceConfig(PdoExtension,
+ &ProgIf,
+ FIELD_OFFSET(PCI_COMMON_HEADER, ProgIf),
+ sizeof(UCHAR));
+
+ /* Verify the setting "stuck" */
+ PciReadDeviceConfig(PdoExtension,
+ &NewProgIf,
+ FIELD_OFFSET(PCI_COMMON_HEADER, ProgIf),
+ sizeof(UCHAR));
+ if (NewProgIf == ProgIf)
+ {
+ /* Update the header and PDO data with the new programming mode */
+ PciData->ProgIf = ProgIf;
+ PdoExtension->ProgIf = NewProgIf;
+
+ /* Clear the first four BARs to reset current BAR setttings */
+ PciData->u.type0.BaseAddresses[0] = 0;
+ PciData->u.type0.BaseAddresses[1] = 0;
+ PciData->u.type0.BaseAddresses[2] = 0;
+ PciData->u.type0.BaseAddresses[3] = 0;
+ PciWriteDeviceConfig(PdoExtension,
+ PciData->u.type0.BaseAddresses,
+ FIELD_OFFSET(PCI_COMMON_HEADER,
+ u.type0.BaseAddresses),
+ 4 * sizeof(ULONG));
+
+ /* Re-read the BARs to have the latest data for native mode IDE */
+ PciReadDeviceConfig(PdoExtension,
+ PciData->u.type0.BaseAddresses,
+ FIELD_OFFSET(PCI_COMMON_HEADER,
+ u.type0.BaseAddresses),
+ 4 * sizeof(ULONG));
+
+ /* Re-read the interrupt pin used for native mode IDE */
+ PciReadDeviceConfig(PdoExtension,
+ &PciData->u.type0.InterruptPin,
+ FIELD_OFFSET(PCI_COMMON_HEADER,
+ u.type0.InterruptPin),
+ sizeof(UCHAR));
+
+ /* The IDE Controller is now in native mode */
+ Switched = TRUE;
+ }
+ else
+ {
+ /* Settings did not work, fail */
+ DPRINT1("PCI: Warning failed switch to native mode for IDE controller VEN_%04x&DEV_%04x!",
+ PciData->VendorID,
+ PciData->DeviceID);
+ }
+ }
+
+ /* Return whether or not native mode was enabled on the IDE controller */
+ return Switched;
+}
+
+VOID
+NTAPI
+PciApplyHacks(IN PPCI_FDO_EXTENSION DeviceExtension,
+ IN PPCI_COMMON_HEADER PciData,
+ IN PCI_SLOT_NUMBER SlotNumber,
+ IN ULONG OperationType,
+ PPCI_PDO_EXTENSION PdoExtension)
+{
+ ULONG LegacyBaseAddress;
+ USHORT Command;
+ UCHAR RegValue;
+
+ UNREFERENCED_PARAMETER(SlotNumber);
+
+ /* Check what kind of hack operation this is */
+ switch (OperationType)
+ {
+ /*
+ * This is mostly concerned with fixing up incorrect class data that can
+ * exist on certain PCI hardware before the 2.0 spec was ratified.
+ */
+ case PCI_HACK_FIXUP_BEFORE_CONFIGURATION:
+
+ /* Note that the i82375 PCI/EISA and the i82378 PCI/ISA bridges that
+ * are present on certain DEC/NT Alpha machines are pre-PCI 2.0 devices
+ * and appear as non-classified, so their correct class/subclass data
+ * is written here instead.
+ */
+ if ((PciData->VendorID == 0x8086) &&
+ ((PciData->DeviceID == 0x482) || (PciData->DeviceID == 0x484)))
+ {
+ /* Note that 0x482 is the i82375 (EISA), 0x484 is the i82378 (ISA) */
+ PciData->SubClass = PciData->DeviceID == 0x482 ?
+ PCI_SUBCLASS_BR_EISA : PCI_SUBCLASS_BR_ISA;
+ PciData->BaseClass = PCI_CLASS_BRIDGE_DEV;
+
+ /*
+ * Because the software is modifying the actual header data from
+ * the BIOS, this flag tells the driver to ignore failures when
+ * comparing the original BIOS data with the PCI data.
+ */
+ if (PdoExtension) PdoExtension->ExpectedWritebackFailure = TRUE;
+ }
+
+ /* Note that in this case, an immediate return is issued */
+ return;
+
+ /*
+ * This is concerned with setting up interrupts correctly for native IDE
+ * mode, but will also handle broken VGA decoding on older bridges as
+ * well as a PAE-specific hack for certain Compaq Hot-Plug Controllers.
+ */
+ case PCI_HACK_FIXUP_AFTER_CONFIGURATION:
+
+ /* There should always be a PDO extension passed in */
+ ASSERT(PdoExtension);
+
+ /*
+ * On the OPTi Viper-M IDE controller, Linux doesn't support IDE-DMA
+ * and FreeBSD bug reports indicate that the system crashes when the
+ * feature is enabled (so it's disabled on that OS as well). In the
+ * NT PCI Bus Driver, it seems Microsoft too, completely disables
+ * Native IDE functionality on this controller, so it would seem OPTi
+ * simply frelled up this controller.
+ */
+ if ((PciData->VendorID == 0x1045) && (PciData->DeviceID != 0xC621))
+ {
+ /* Disable native mode */
+ PciData->ProgIf &= ~5;
+ PciData->u.type0.InterruptPin = 0;
+
+ /*
+ * Because the software is modifying the actual header data from
+ * the BIOS, this flag tells the driver to ignore failures when
+ * comparing the original BIOS data with the PCI data.
+ */
+ PdoExtension->ExpectedWritebackFailure = TRUE;
+ }
+ else if ((PciData->BaseClass == PCI_CLASS_MASS_STORAGE_CTLR) &&
+ (PciData->SubClass == PCI_SUBCLASS_MSC_IDE_CTLR))
+ {
+ /* For other IDE controllers, start out in compatible mode */
+ PdoExtension->BIOSAllowsIDESwitchToNativeMode = FALSE;
+
+ /*
+ * Registry must have enabled native mode (typically as a result
+ * of an INF file directive part of the IDE controller's driver)
+ * and the system must not be booted in Safe Mode. If that checks
+ * out, then evaluate the ACPI NATA method to see if the platform
+ * supports this. See the section "BIOS and Platform Prerequisites
+ * for Switching a Native-Mode-Capable Controller" in the Storage
+ * section of the Windows Driver Kit for more details:
+ *
+ * 5. For each ATA controller enumerated, the PCI bus driver checks
+ * the Programming Interface register of the IDE controller to
+ * see if it supports switching both channels to native mode.
+ * 6. The PCI bus driver checks whether the BIOS/platform supports
+ * switching the controller by checking the NATA method described
+ * earlier in this article.
+ *
+ * If an ATA controller does not indicate that it is native
+ * mode-capable, or if the BIOS NATA control method is missing
+ * or does not list that device, the PCI bus driver does not
+ * switch the controller and it is assigned legacy resources.
+ *
+ * If both the controller and the BIOS indicate that the controller
+ * can be switched, the process of switching the controller begins
+ * with the next step.
+ */
+ if ((PciEnableNativeModeATA) &&
+ !(InitSafeBootMode) &&
+ (PciIsSlotPresentInParentMethod(PdoExtension, 'ATAN')))
+ {
+ /* The platform supports it, remember that */
+ PdoExtension->BIOSAllowsIDESwitchToNativeMode = TRUE;
+
+ /*
+ * Now switch the controller into native mode if both channels
+ * support native IDE mode. See "How Windows Switches an ATA
+ * Controller to Native Mode" in the Storage section of the
+ * Windows Driver Kit for more details.
+ */
+ PdoExtension->IDEInNativeMode =
+ PciConfigureIdeController(PdoExtension, PciData, TRUE);
+ }
+
+ /* Is native mode enabled after all? */
+ if ((PciData->ProgIf & 5) != 5)
+ {
+ /* Compatible mode, so force ISA-style IRQ14 and IRQ 15 */
+ PciData->u.type0.InterruptPin = 0;
+ }
+ }
+
+ /* Is this a PCI device with legacy VGA card decodes on the root bus? */
+ if ((PdoExtension->HackFlags & PCI_HACK_VIDEO_LEGACY_DECODE) &&
+ (PCI_IS_ROOT_FDO(DeviceExtension)) &&
+ !(DeviceExtension->BrokenVideoHackApplied))
+ {
+ /* Tell the arbiter to apply a hack for these older devices */
+ ario_ApplyBrokenVideoHack(DeviceExtension);
+ }
+
+ /* Is this a Compaq PCI Hotplug Controller (r17) on a PAE system ? */
+ if ((PciData->VendorID == 0xE11) &&
+ (PciData->DeviceID == 0xA0F7) &&
+ (PciData->RevisionID == 17) &&
+ (ExIsProcessorFeaturePresent(PF_PAE_ENABLED)))
+ {
+ /* Turn off the decodes immediately */
+ PciData->Command &= ~(PCI_ENABLE_IO_SPACE |
+ PCI_ENABLE_MEMORY_SPACE |
+ PCI_ENABLE_BUS_MASTER);
+ PciWriteDeviceConfig(PdoExtension,
+ &PciData->Command,
+ FIELD_OFFSET(PCI_COMMON_HEADER, Command),
+ sizeof(USHORT));
+
+ /* Do not EVER turn them on again, this will blow up the system */
+ PdoExtension->CommandEnables &= ~(PCI_ENABLE_IO_SPACE |
+ PCI_ENABLE_MEMORY_SPACE |
+ PCI_ENABLE_BUS_MASTER);
+ PdoExtension->HackFlags |= PCI_HACK_PRESERVE_COMMAND;
+ }
+ break;
+
+ /*
+ * This is called whenever resources are changed and hardware needs to be
+ * updated. It is concerned with two highly specific erratas on an IBM
+ * hot-plug docking bridge used on the Thinkpad 600 Series and on Intel's
+ * ICH PCI Bridges.
+ */
+ case PCI_HACK_FIXUP_BEFORE_UPDATE:
+
+ /* There should always be a PDO extension passed in */
+ ASSERT(PdoExtension);
+
+ /* Is this an IBM 20H2999 PCI Docking Bridge, used on Thinkpads? */
+ if ((PdoExtension->VendorId == 0x1014) &&
+ (PdoExtension->DeviceId == 0x95))
+ {
+ /* Read the current command */
+ PciReadDeviceConfig(PdoExtension,
+ &Command,
+ FIELD_OFFSET(PCI_COMMON_HEADER, Command),
+ sizeof(USHORT));
+
+ /* Turn off the decodes */
+ PciDecodeEnable(PdoExtension, FALSE, &Command);
+
+ /* Apply the required IBM workaround */
+ PciReadDeviceConfig(PdoExtension, &RegValue, 0xE0, sizeof(UCHAR));
+ RegValue &= ~2;
+ RegValue |= 1;
+ PciWriteDeviceConfig(PdoExtension, &RegValue, 0xE0, sizeof(UCHAR));
+
+ /* Restore the command to its original value */
+ PciWriteDeviceConfig(PdoExtension,
+ &Command,
+ FIELD_OFFSET(PCI_COMMON_HEADER, Command),
+ sizeof(USHORT));
+
+ }
+
+ /*
+ * Check for Intel ICH PCI-to-PCI (i82801) bridges (used on the i810,
+ * i820, i840, i845 Chipsets) that have subtractive decode enabled,
+ * and whose hack flags do not specifiy that this support is broken.
+ */
+ if ((PdoExtension->HeaderType == PCI_BRIDGE_TYPE) &&
+ (PdoExtension->Dependent.type1.SubtractiveDecode) &&
+ ((PdoExtension->VendorId == 0x8086) &&
+ ((PdoExtension->DeviceId == 0x2418) ||
+ (PdoExtension->DeviceId == 0x2428) ||
+ (PdoExtension->DeviceId == 0x244E) ||
+ (PdoExtension->DeviceId == 0x2448))) &&
+ !(PdoExtension->HackFlags & PCI_HACK_BROKEN_SUBTRACTIVE_DECODE))
+ {
+ /*
+ * The positive decode window shouldn't be used, these values are
+ * normally all read-only or initialized to 0 by the BIOS, but
+ * it appears Intel doesn't do this, so the PCI Bus Driver will
+ * do it in software instead. Note that this is used to prevent
+ * certain non-compliant PCI devices from breaking down due to the
+ * fact that these ICH bridges have a known "quirk" (which Intel
+ * documents as a known "erratum", although it's not not really
+ * an ICH bug since the PCI specification does allow for it) in
+ * that they will sometimes send non-zero addresses during special
+ * cycles (ie: non-zero data during the address phase). These
+ * broken PCI cards will mistakenly attempt to claim the special
+ * cycle and corrupt their I/O and RAM ranges. Again, in Intel's
+ * defense, the PCI specification only requires stable data, not
+ * necessarily zero data, during the address phase.
+ */
+ PciData->u.type1.MemoryBase = 0xFFFF;
+ PciData->u.type1.PrefetchBase = 0xFFFF;
+ PciData->u.type1.IOBase = 0xFF;
+ PciData->u.type1.IOLimit = 0;
+ PciData->u.type1.MemoryLimit = 0;
+ PciData->u.type1.PrefetchLimit = 0;
+ PciData->u.type1.PrefetchBaseUpper32 = 0;
+ PciData->u.type1.PrefetchLimitUpper32 = 0;
+ PciData->u.type1.IOBaseUpper16 = 0;
+ PciData->u.type1.IOLimitUpper16 = 0;
+ }
+ break;
+
+ default:
+ return;
+ }
+
+ /* Finally, also check if this is this a CardBUS device? */
+ if (PCI_CONFIGURATION_TYPE(PciData) == PCI_CARDBUS_BRIDGE_TYPE)
+ {
+ /*
+ * At offset 44h the LegacyBaseAddress is stored, which is cleared by
+ * ACPI-aware versions of Windows, to disable legacy-mode I/O access to
+ * CardBus controllers. For more information, see "Supporting CardBus
+ * Controllers under ACPI" in the "CardBus Controllers and Windows"
+ * Whitepaper on WHDC.
+ */
+ LegacyBaseAddress = 0;
+ PciWriteDeviceConfig(PdoExtension,
+ &LegacyBaseAddress,
+ sizeof(PCI_COMMON_HEADER) + sizeof(ULONG),
+ sizeof(ULONG));
+ }
+}
+
+BOOLEAN
+NTAPI
+PcipIsSameDevice(IN PPCI_PDO_EXTENSION DeviceExtension,
+ IN PPCI_COMMON_HEADER PciData)
+{
+ BOOLEAN IdMatch, RevMatch, SubsysMatch;
+ ULONGLONG HackFlags = DeviceExtension->HackFlags;
+
+ /* Check if the IDs match */
+ IdMatch = (PciData->VendorID == DeviceExtension->VendorId) &&
+ (PciData->DeviceID == DeviceExtension->DeviceId);
+ if (!IdMatch) return FALSE;
+
+ /* If the device has a valid revision, check if it matches */
+ RevMatch = (HackFlags & PCI_HACK_NO_REVISION_AFTER_D3) ||
+ (PciData->RevisionID == DeviceExtension->RevisionId);
+ if (!RevMatch) return FALSE;
+
+ /* For multifunction devices, this is enough to assume they're the same */
+ if (PCI_MULTIFUNCTION_DEVICE(PciData)) return TRUE;
+
+ /* For bridge devices, there's also nothing else that can be checked */
+ if (DeviceExtension->BaseClass == PCI_CLASS_BRIDGE_DEV) return TRUE;
+
+ /* Devices, on the other hand, have subsystem data that can be compared */
+ SubsysMatch = (HackFlags & (PCI_HACK_NO_SUBSYSTEM |
+ PCI_HACK_NO_SUBSYSTEM_AFTER_D3)) ||
+ ((DeviceExtension->SubsystemVendorId ==
+ PciData->u.type0.SubVendorID) &&
+ (DeviceExtension->SubsystemId ==
+ PciData->u.type0.SubSystemID));
+ return SubsysMatch;
+}
+
+BOOLEAN
+NTAPI
+PciSkipThisFunction(IN PPCI_COMMON_HEADER PciData,
+ IN PCI_SLOT_NUMBER Slot,
+ IN UCHAR OperationType,
+ IN ULONGLONG HackFlags)
+{
+ do
+ {
+ /* Check if this is device enumeration */
+ if (OperationType == PCI_SKIP_DEVICE_ENUMERATION)
+ {
+ /* Check if there's a hackflag saying not to enumerate this device */
+ if (HackFlags & PCI_HACK_NO_ENUM_AT_ALL) break;
+
+ /* Check if this is the high end of a double decker device */
+ if ((HackFlags & PCI_HACK_DOUBLE_DECKER) &&
+ (Slot.u.bits.DeviceNumber >= 16))
+ {
+ /* It belongs to the same device, so skip it */
+ DPRINT1(" Device (Ven %04x Dev %04x (d=0x%x, f=0x%x)) is a ghost.\n",
+ PciData->VendorID,
+ PciData->DeviceID,
+ Slot.u.bits.DeviceNumber,
+ Slot.u.bits.FunctionNumber);
+ break;
+ }
+ }
+ else if (OperationType == PCI_SKIP_RESOURCE_ENUMERATION)
+ {
+ /* Resource enumeration, check for a hackflag saying not to do it */
+ if (HackFlags & PCI_HACK_ENUM_NO_RESOURCE) break;
+ }
+ else
+ {
+ /* Logic error in the driver */
+ ASSERTMSG("PCI Skip Function - Operation type unknown.", FALSE);
+ }
+
+ /* Check for legacy bridges during resource enumeration */
+ if ((PciData->BaseClass == PCI_CLASS_BRIDGE_DEV) &&
+ (PciData->SubClass <= PCI_SUBCLASS_BR_MCA) &&
+ (OperationType == PCI_SKIP_RESOURCE_ENUMERATION))
+ {
+ /* Their resources are not enumerated, only PCI and Cardbus/PCMCIA */
+ break;
+ }
+ else if (PciData->BaseClass == PCI_CLASS_NOT_DEFINED)
+ {
+ /* Undefined base class (usually a PCI BIOS/ROM bug) */
+ DPRINT1(" Vendor %04x, Device %04x has class code of PCI_CLASS_NOT_DEFINED\n",
+ PciData->VendorID,
+ PciData->DeviceID);
+
+ /*
+ * The Alder has an Intel Extended Express System Support Controller
+ * which presents apparently spurious BARs. When the PCI resource
+ * code tries to reassign these BARs, the second IO-APIC gets
+ * disabled (with disastrous consequences). The first BAR is the
+ * actual IO-APIC, the remaining five bars seem to be spurious
+ * resources, so ignore this device completely.
+ */
+ if ((PciData->VendorID == 0x8086) && (PciData->DeviceID == 8)) break;
+ }
+
+ /* Other normal PCI cards and bridges are enumerated */
+ if (PCI_CONFIGURATION_TYPE(PciData) <= PCI_CARDBUS_BRIDGE_TYPE) return FALSE;
+ } while (FALSE);
+
+ /* Hit one of the known bugs/hackflags, or this is a new kind of PCI unit */
+ DPRINT1(" Device skipped (not enumerated).\n");
+ return TRUE;
+}
+
+VOID
+NTAPI
+PciGetEnhancedCapabilities(IN PPCI_PDO_EXTENSION PdoExtension,
+ IN PPCI_COMMON_HEADER PciData)
+{
+ ULONG HeaderType, CapPtr, TargetAgpCapabilityId;
+ DEVICE_POWER_STATE WakeLevel;
+ PCI_CAPABILITIES_HEADER AgpCapability;
+ PCI_PM_CAPABILITY PowerCapabilities;
+ PAGED_CODE();
+
+ /* Assume no known wake level */
+ PdoExtension->PowerState.DeviceWakeLevel = PowerDeviceUnspecified;
+
+ /* Make sure the device has capabilities */
+ if (!(PciData->Status & PCI_STATUS_CAPABILITIES_LIST))
+ {
+ /* If it doesn't, there will be no power management */
+ PdoExtension->CapabilitiesPtr = 0;
+ PdoExtension->HackFlags |= PCI_HACK_NO_PM_CAPS;
+ }
+ else
+ {
+ /* There's capabilities, need to figure out where to get the offset */
+ HeaderType = PCI_CONFIGURATION_TYPE(PciData);
+ if (HeaderType == PCI_CARDBUS_BRIDGE_TYPE)
+ {
+ /* Use the bridge's header */
+ CapPtr = PciData->u.type2.CapabilitiesPtr;
+ }
+ else
+ {
+ /* Use the device header */
+ ASSERT(HeaderType <= PCI_CARDBUS_BRIDGE_TYPE);
+ CapPtr = PciData->u.type0.CapabilitiesPtr;
+ }
+
+ /* Skip garbage capabilities pointer */
+ if (((CapPtr & 0x3) != 0) || (CapPtr < PCI_COMMON_HDR_LENGTH))
+ {
+ /* Report no extended capabilities */
+ PdoExtension->CapabilitiesPtr = 0;
+ PdoExtension->HackFlags |= PCI_HACK_NO_PM_CAPS;
+ }
+ else
+ {
+ DPRINT1("Device has capabilities at: %lx\n", CapPtr);
+ PdoExtension->CapabilitiesPtr = CapPtr;
+
+ /* Check for PCI-to-PCI Bridges and AGP bridges */
+ if ((PdoExtension->BaseClass == PCI_CLASS_BRIDGE_DEV) &&
+ ((PdoExtension->SubClass == PCI_SUBCLASS_BR_HOST) ||
+ (PdoExtension->SubClass == PCI_SUBCLASS_BR_PCI_TO_PCI)))
+ {
+ /* Query either the raw AGP capabilitity, or the Target AGP one */
+ TargetAgpCapabilityId = (PdoExtension->SubClass ==
+ PCI_SUBCLASS_BR_PCI_TO_PCI) ?
+ PCI_CAPABILITY_ID_AGP_TARGET :
+ PCI_CAPABILITY_ID_AGP;
+ if (PciReadDeviceCapability(PdoExtension,
+ PdoExtension->CapabilitiesPtr,
+ TargetAgpCapabilityId,
+ &AgpCapability,
+ sizeof(PCI_CAPABILITIES_HEADER)))
+ {
+ /* AGP target ID was found, store it */
+ DPRINT1("AGP ID: %lx\n", TargetAgpCapabilityId);
+ PdoExtension->TargetAgpCapabilityId = TargetAgpCapabilityId;
+ }
+ }
+
+ /* Check for devices that are known not to have proper power management */
+ if (!(PdoExtension->HackFlags & PCI_HACK_NO_PM_CAPS))
+ {
+ /* Query if this device supports power management */
+ if (!PciReadDeviceCapability(PdoExtension,
+ PdoExtension->CapabilitiesPtr,
+ PCI_CAPABILITY_ID_POWER_MANAGEMENT,
+ &PowerCapabilities.Header,
+ sizeof(PCI_PM_CAPABILITY)))
+ {
+ /* No power management, so act as if it had the hackflag set */
+ DPRINT1("No PM caps, disabling PM\n");
+ PdoExtension->HackFlags |= PCI_HACK_NO_PM_CAPS;
+ }
+ else
+ {
+ /* Otherwise, pick the highest wake level that is supported */
+ WakeLevel = PowerDeviceUnspecified;
+ if (PowerCapabilities.PMC.Capabilities.Support.PMED0)
+ WakeLevel = PowerDeviceD0;
+ if (PowerCapabilities.PMC.Capabilities.Support.PMED1)
+ WakeLevel = PowerDeviceD1;
+ if (PowerCapabilities.PMC.Capabilities.Support.PMED2)
+ WakeLevel = PowerDeviceD2;
+ if (PowerCapabilities.PMC.Capabilities.Support.PMED3Hot)
+ WakeLevel = PowerDeviceD3;
+ if (PowerCapabilities.PMC.Capabilities.Support.PMED3Cold)
+ WakeLevel = PowerDeviceD3;
+ PdoExtension->PowerState.DeviceWakeLevel = WakeLevel;
+
+ /* Convert the PCI power state to the NT power state */
+ PdoExtension->PowerState.CurrentDeviceState =
+ PowerCapabilities.PMCSR.ControlStatus.PowerState + 1;
+
+ /* Save all the power capabilities */
+ PdoExtension->PowerCapabilities = PowerCapabilities.PMC.Capabilities;
+ DPRINT1("PM Caps Found! Wake Level: %d Power State: %d\n",
+ WakeLevel, PdoExtension->PowerState.CurrentDeviceState);
+ }
+ }
+ }
+ }
+
+ /* At the very end of all this, does this device not have power management? */
+ if (PdoExtension->HackFlags & PCI_HACK_NO_PM_CAPS)
+ {
+ /* Then guess the current state based on whether the decodes are on */
+ PdoExtension->PowerState.CurrentDeviceState =
+ PciData->Command & (PCI_ENABLE_IO_SPACE |
+ PCI_ENABLE_MEMORY_SPACE |
+ PCI_ENABLE_BUS_MASTER) ?
+ PowerDeviceD0: PowerDeviceD3;
+ DPRINT1("PM is off, so assumed device is: %d based on enables\n",
+ PdoExtension->PowerState.CurrentDeviceState);
+ }
+}
+
+VOID
+NTAPI
+PciWriteLimitsAndRestoreCurrent(IN PVOID Reserved,
+ IN PVOID Context2)
+{
+ PPCI_CONFIGURATOR_CONTEXT Context = Context2;
+ PPCI_COMMON_HEADER PciData, Current;
+ PPCI_PDO_EXTENSION PdoExtension;
+
+ UNREFERENCED_PARAMETER(Reserved);
+
+ /* Grab all parameters from the context */
+ PdoExtension = Context->PdoExtension;
+ Current = Context->Current;
+ PciData = Context->PciData;
+
+ /* Write the limit discovery header */
+ PciWriteDeviceConfig(PdoExtension, PciData, 0, PCI_COMMON_HDR_LENGTH);
+
+ /* Now read what the device indicated the limits are */
+ PciReadDeviceConfig(PdoExtension, PciData, 0, PCI_COMMON_HDR_LENGTH);
+
+ /* Then write back the original configuration header */
+ PciWriteDeviceConfig(PdoExtension, Current, 0, PCI_COMMON_HDR_LENGTH);
+
+ /* Copy back the original command that was saved in the context */
+ Current->Command = Context->Command;
+ if (Context->Command)
+ {
+ /* Program it back into the device */
+ PciWriteDeviceConfig(PdoExtension,
+ &Context->Command,
+ FIELD_OFFSET(PCI_COMMON_HEADER, Command),
+ sizeof(USHORT));
+ }
+
+ /* Copy back the original status that was saved as well */
+ Current->Status = Context->Status;
+
+ /* Call the configurator to restore any other data that might've changed */
+ Context->Configurator->RestoreCurrent(Context);
+}
+
+NTSTATUS
+NTAPI
+PcipGetFunctionLimits(IN PPCI_CONFIGURATOR_CONTEXT Context)
+{
+ PPCI_CONFIGURATOR Configurator;
+ PPCI_COMMON_HEADER PciData, Current;
+ PPCI_PDO_EXTENSION PdoExtension;
+ PCI_IPI_CONTEXT IpiContext;
+ PIO_RESOURCE_DESCRIPTOR IoDescriptor;
+ ULONG Offset;
+ PAGED_CODE();
+
+ /* Grab all parameters from the context */
+ PdoExtension = Context->PdoExtension;
+ Current = Context->Current;
+ PciData = Context->PciData;
+
+ /* Save the current PCI Command and Status word */
+ Context->Status = Current->Status;
+ Context->Command = Current->Command;
+
+ /* Now that they're saved, clear the status, and disable all decodes */
+ Current->Status = 0;
+ Current->Command &= ~(PCI_ENABLE_IO_SPACE |
+ PCI_ENABLE_MEMORY_SPACE |
+ PCI_ENABLE_BUS_MASTER);
+
+ /* Make a copy of the current PCI configuration header (with decodes off) */
+ RtlCopyMemory(PciData, Current, PCI_COMMON_HDR_LENGTH);
+
+ /* Locate the correct resource configurator for this type of device */
+ Configurator = &PciConfigurators[PdoExtension->HeaderType];
+ Context->Configurator = Configurator;
+
+ /* Initialize it, which will typically setup the BARs for limit discovery */
+ Configurator->Initialize(Context);
+
+ /* Check for critical devices and PCI Debugging devices */
+ if ((PdoExtension->HackFlags & PCI_HACK_CRITICAL_DEVICE) ||
+ (PdoExtension->OnDebugPath))
+ {
+ /* Specifically check for a PCI Debugging device */
+ if (PdoExtension->OnDebugPath)
+ {
+ /* Was it enabled for bus mastering? */
+ if (Context->Command & PCI_ENABLE_BUS_MASTER)
+ {
+ /* This decode needs to be re-enabled so debugging can work */
+ PciData->Command |= PCI_ENABLE_BUS_MASTER;
+ Current->Command |= PCI_ENABLE_BUS_MASTER;
+ }
+
+ /* Disable the debugger while the discovery is happening */
+ KdDisableDebugger();
+ }
+
+ /* For these devices, an IPI must be sent to force high-IRQL discovery */
+ IpiContext.Barrier = 1;
+ IpiContext.RunCount = 1;
+ IpiContext.DeviceExtension = PdoExtension;
+ IpiContext.Function = PciWriteLimitsAndRestoreCurrent;
+ IpiContext.Context = Context;
+ KeIpiGenericCall(PciExecuteCriticalSystemRoutine, (ULONG_PTR)&IpiContext);
+
+ /* Re-enable the debugger if this was a PCI Debugging Device */
+ if (PdoExtension->OnDebugPath) KdEnableDebugger();
+ }
+ else
+ {
+ /* Otherwise, it's safe to do this in-line at low IRQL */
+ PciWriteLimitsAndRestoreCurrent(PdoExtension, Context);
+ }
+
+ /*
+ * Check if it's valid to compare the headers to see if limit discovery mode
+ * has properly exited (the expected case is that the PCI header would now
+ * be equal to what it was before). In some cases, it is known that this will
+ * fail, because during PciApplyHacks (among other places), software hacks
+ * had to be applied to the header, which the hardware-side will not see, and
+ * thus the headers would appear "different".
+ */
+ if (!PdoExtension->ExpectedWritebackFailure)
+ {
+ /* Read the current PCI header now, after discovery has completed */
+ PciReadDeviceConfig(PdoExtension, PciData + 1, 0, PCI_COMMON_HDR_LENGTH);
+
+ /* Check if the current header at entry, is equal to the header now */
+ Offset = RtlCompareMemory(PciData + 1, Current, PCI_COMMON_HDR_LENGTH);
+ if (Offset != PCI_COMMON_HDR_LENGTH)
+ {
+ /* It's not, which means configuration somehow changed, dump this */
+ DPRINT1("PCI - CFG space write verify failed at offset 0x%x\n", Offset);
+ PciDebugDumpCommonConfig(PciData + 1);
+ DPRINT1("----------\n");
+ PciDebugDumpCommonConfig(Current);
+ }
+ }
+
+ /* This PDO should not already have resources, since this is only done once */
+ ASSERT(PdoExtension->Resources == NULL);
+
+ /* Allocate the structure that will hold the discovered resources and limits */
+ PdoExtension->Resources = ExAllocatePoolWithTag(NonPagedPool,
+ sizeof(PCI_FUNCTION_RESOURCES),
+ 'BicP');
+ if (!PdoExtension->Resources) return STATUS_INSUFFICIENT_RESOURCES;
+
+ /* Clear it out for now */
+ RtlZeroMemory(PdoExtension->Resources, sizeof(PCI_FUNCTION_RESOURCES));
+
+ /* Now call the configurator, which will first store the limits... */
+ Configurator->SaveLimits(Context);
+
+ /* ...and then store the current resources being used */
+ Configurator->SaveCurrentSettings(Context);
+
+ /* Loop all the limit descriptors backwards */
+ IoDescriptor = &PdoExtension->Resources->Limit[PCI_TYPE0_ADDRESSES + 1];
+ while (TRUE)
+ {
+ /* Keep going until a non-null descriptor is found */
+ IoDescriptor--;
+ if (IoDescriptor->Type != CmResourceTypeNull) break;
+
+ /* This is a null descriptor, is it the last one? */
+ if (IoDescriptor == &PdoExtension->Resources->Limit[PCI_TYPE0_ADDRESSES + 1])
+ {
+ /* This means the descriptor is NULL, which means discovery failed */
+ DPRINT1("PCI Resources fail!\n");
+
+ /* No resources will be assigned for the device */
+ ExFreePoolWithTag(PdoExtension->Resources, 0);
+ PdoExtension->Resources = NULL;
+ break;
+ }
+ }
+
+ /* Return success here, even if the device has no assigned resources */
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS
+NTAPI
+PciGetFunctionLimits(IN PPCI_PDO_EXTENSION PdoExtension,
+ IN PPCI_COMMON_HEADER Current,
+ IN ULONGLONG HackFlags)
+{
+ NTSTATUS Status;
+ PPCI_COMMON_HEADER PciData;
+ PCI_CONFIGURATOR_CONTEXT Context;
+ PAGED_CODE();
+
+ /* Do the hackflags indicate this device should be skipped? */
+ if (PciSkipThisFunction(Current,
+ PdoExtension->Slot,
+ PCI_SKIP_RESOURCE_ENUMERATION,
+ HackFlags))
+ {
+ /* Do not process its resources */
+ return STATUS_SUCCESS;
+ }
+
+ /* Allocate a buffer to hold two PCI configuration headers */
+ PciData = ExAllocatePoolWithTag(0, 2 * PCI_COMMON_HDR_LENGTH, 'BicP');
+ if (!PciData) return STATUS_INSUFFICIENT_RESOURCES;
+
+ /* Set up the context for the resource enumeration, and do it */
+ Context.Current = Current;
+ Context.PciData = PciData;
+ Context.PdoExtension = PdoExtension;
+ Status = PcipGetFunctionLimits(&Context);
+
+ /* Enumeration is completed, free the PCI headers and return the status */
+ ExFreePoolWithTag(PciData, 0);
+ return Status;
+}
+
+VOID
+NTAPI
+PciProcessBus(IN PPCI_FDO_EXTENSION DeviceExtension)
+{
+ PPCI_PDO_EXTENSION PdoExtension;
+ PDEVICE_OBJECT PhysicalDeviceObject;
+ PAGED_CODE();
+
+ /* Get the PDO Extension */
+ PhysicalDeviceObject = DeviceExtension->PhysicalDeviceObject;
+ PdoExtension = (PPCI_PDO_EXTENSION)PhysicalDeviceObject->DeviceExtension;
+
+ /* Cheeck if this is the root bus */
+ if (!PCI_IS_ROOT_FDO(DeviceExtension))
+ {
+ /* Not really handling this year */
+ UNIMPLEMENTED_DBGBREAK();
+
+ /* Check for PCI bridges with the ISA bit set, or required */
+ if ((PdoExtension) &&
+ (PciClassifyDeviceType(PdoExtension) == PciTypePciBridge) &&
+ ((PdoExtension->Dependent.type1.IsaBitRequired) ||
+ (PdoExtension->Dependent.type1.IsaBitSet)))
+ {
+ /* We'll need to do some legacy support */
+ UNIMPLEMENTED_DBGBREAK();
+ }
+ }
+ else
+ {
+ /* Scan all of the root bus' children bridges */
+ for (PdoExtension = DeviceExtension->ChildBridgePdoList;
+ PdoExtension;
+ PdoExtension = PdoExtension->NextBridge)
+ {
+ /* Find any that have the VGA decode bit on */
+ if (PdoExtension->Dependent.type1.VgaBitSet)
+ {
+ /* Again, some more legacy support we'll have to do */
+ UNIMPLEMENTED_DBGBREAK();
+ }
+ }
+ }
+
+ /* Check for ACPI systems where the OS assigns bus numbers */
+ if (PciAssignBusNumbers)
+ {
+ /* Not yet supported */
+ UNIMPLEMENTED_DBGBREAK();
+ }
+}
+
+NTSTATUS
+NTAPI
+PciScanBus(IN PPCI_FDO_EXTENSION DeviceExtension)
+{
+ ULONG MaxDevice = PCI_MAX_DEVICES;
+ BOOLEAN ProcessFlag = FALSE;
+ ULONG i, j, k, Size;
+ USHORT CapOffset, TempOffset;
+ LONGLONG HackFlags;
+ PDEVICE_OBJECT DeviceObject;
+ UCHAR Buffer[PCI_COMMON_HDR_LENGTH];
+ UCHAR BiosBuffer[PCI_COMMON_HDR_LENGTH];
+ PPCI_COMMON_HEADER PciData = (PVOID)Buffer;
+ PPCI_COMMON_HEADER BiosData = (PVOID)BiosBuffer;
+ PCI_SLOT_NUMBER PciSlot;
+ PCHAR Name;
+ NTSTATUS Status;
+ PPCI_PDO_EXTENSION PdoExtension, NewExtension;
+ PPCI_PDO_EXTENSION* BridgeExtension;
+ PWCHAR DescriptionText;
+ USHORT SubVendorId, SubSystemId;
+ PCI_CAPABILITIES_HEADER CapHeader, PcixCapHeader;
+ UCHAR SecondaryBus;
+ DPRINT1("PCI Scan Bus: FDO Extension @ 0x%p, Base Bus = 0x%x\n",
+ DeviceExtension, DeviceExtension->BaseBus);
+
+ /* Is this the root FDO? */
+ if (!PCI_IS_ROOT_FDO(DeviceExtension))
+ {
+ /* Get the PDO for the child bus */
+ PdoExtension = DeviceExtension->PhysicalDeviceObject->DeviceExtension;
+ ASSERT_PDO(PdoExtension);
+
+ /* Check for hack which only allows bus to have one child device */
+ if (PdoExtension->HackFlags & PCI_HACK_ONE_CHILD) MaxDevice = 1;
+
+ /* Check if the secondary bus number has changed */
+ PciReadDeviceConfig(PdoExtension,
+ &SecondaryBus,
+ FIELD_OFFSET(PCI_COMMON_HEADER, u.type1.SecondaryBus),
+ sizeof(UCHAR));
+ if (SecondaryBus != PdoExtension->Dependent.type1.SecondaryBus)
+ {
+ UNIMPLEMENTED_DBGBREAK("PCI: Bus numbers have been changed! Restoring originals.\n");
+ }
+ }
+
+ /* Loop every device on the bus */
+ PciSlot.u.bits.Reserved = 0;
+ i = DeviceExtension->BaseBus;
+ for (j = 0; j < MaxDevice; j++)
+ {
+ /* Loop every function of each device */
+ PciSlot.u.bits.DeviceNumber = j;
+ for (k = 0; k < PCI_MAX_FUNCTION; k++)
+ {
+ /* Build the final slot structure */
+ PciSlot.u.bits.FunctionNumber = k;
+
+ /* Read the vendor for this slot */
+ PciReadSlotConfig(DeviceExtension,
+ PciSlot,
+ PciData,
+ 0,
+ sizeof(USHORT));
+
+ /* Skip invalid device */
+ if (PciData->VendorID == PCI_INVALID_VENDORID) continue;
+
+ /* Now read the whole header */
+ PciReadSlotConfig(DeviceExtension,
+ PciSlot,
+ &PciData->DeviceID,
+ sizeof(USHORT),
+ PCI_COMMON_HDR_LENGTH - sizeof(USHORT));
+
+ /* Apply any hacks before even analyzing the configuration header */
+ PciApplyHacks(DeviceExtension,
+ PciData,
+ PciSlot,
+ PCI_HACK_FIXUP_BEFORE_CONFIGURATION,
+ NULL);
+
+ /* Dump device that was found */
+ DPRINT1("Scan Found Device 0x%x (b=0x%x, d=0x%x, f=0x%x)\n",
+ PciSlot.u.AsULONG,
+ i,
+ j,
+ k);
+
+ /* Dump the device's header */
+ PciDebugDumpCommonConfig(PciData);
+
+ /* Find description for this device for the debugger's sake */
+ DescriptionText = PciGetDeviceDescriptionMessage(PciData->BaseClass,
+ PciData->SubClass);
+ DPRINT1("Device Description \"%S\".\n",
+ DescriptionText ? DescriptionText : L"(NULL)");
+ if (DescriptionText) ExFreePoolWithTag(DescriptionText, 0);
+
+ /* Check if there is an ACPI Watchdog Table */
+ if (WdTable)
+ {
+ /* Check if this PCI device is the ACPI Watchdog Device... */
+ UNIMPLEMENTED_DBGBREAK();
+ }
+
+ /* Check for non-simple devices */
+ if ((PCI_MULTIFUNCTION_DEVICE(PciData)) ||
+ (PciData->BaseClass == PCI_CLASS_BRIDGE_DEV))
+ {
+ /* No subsystem data defined for these kinds of bridges */
+ SubVendorId = 0;
+ SubSystemId = 0;
+ }
+ else
+ {
+ /* Read the subsystem information from the PCI header */
+ SubVendorId = PciData->u.type0.SubVendorID;
+ SubSystemId = PciData->u.type0.SubSystemID;
+ }
+
+ /* Get any hack flags for this device */
+ HackFlags = PciGetHackFlags(PciData->VendorID,
+ PciData->DeviceID,
+ SubVendorId,
+ SubSystemId,
+ PciData->RevisionID);
+
+ /* Check if this device is considered critical by the OS */
+ if (PciIsCriticalDeviceClass(PciData->BaseClass, PciData->SubClass))
+ {
+ /* Check if normally the decodes would be disabled */
+ if (!(HackFlags & PCI_HACK_DONT_DISABLE_DECODES))
+ {
+ /* Because this device is critical, don't disable them */
+ DPRINT1("Not allowing PM Because device is critical\n");
+ HackFlags |= PCI_HACK_CRITICAL_DEVICE;
+ }
+ }
+
+ /* PCI bridges with a VGA card are also considered critical */
+ if ((PciData->BaseClass == PCI_CLASS_BRIDGE_DEV) &&
+ (PciData->SubClass == PCI_SUBCLASS_BR_PCI_TO_PCI) &&
+ (PciData->u.type1.BridgeControl & PCI_ENABLE_BRIDGE_VGA) &&
+ !(HackFlags & PCI_HACK_DONT_DISABLE_DECODES))
+ {
+ /* Do not disable their decodes either */
+ DPRINT1("Not allowing PM because device is VGA\n");
+ HackFlags |= PCI_HACK_CRITICAL_DEVICE;
+ }
+
+ /* Check if the device should be skipped for whatever reason */
+ if (PciSkipThisFunction(PciData,
+ PciSlot,
+ PCI_SKIP_DEVICE_ENUMERATION,
+ HackFlags))
+ {
+ /* Skip this device */
+ continue;
+ }
+
+ /* Check if a PDO has already been created for this device */
+ PdoExtension = PciFindPdoByFunction(DeviceExtension,
+ PciSlot.u.AsULONG,
+ PciData);
+ if (PdoExtension)
+ {
+ /* Rescan scenarios are not yet implemented */
+ UNIMPLEMENTED_DBGBREAK();
+ }
+
+ /* Bus processing will need to happen */
+ ProcessFlag = TRUE;
+
+ /* Create the PDO for this device */
+ Status = PciPdoCreate(DeviceExtension, PciSlot, &DeviceObject);
+ ASSERT(NT_SUCCESS(Status));
+ NewExtension = (PPCI_PDO_EXTENSION)DeviceObject->DeviceExtension;
+
+ /* Check for broken devices with wrong/no class codes */
+ if (HackFlags & PCI_HACK_FAKE_CLASS_CODE)
+ {
+ /* Setup a default one */
+ PciData->BaseClass = PCI_CLASS_BASE_SYSTEM_DEV;
+ PciData->SubClass = PCI_SUBCLASS_SYS_OTHER;
+
+ /* Device will behave erratically when reading back data */
+ NewExtension->ExpectedWritebackFailure = TRUE;
+ }
+
+ /* Clone all the information from the header */
+ NewExtension->VendorId = PciData->VendorID;
+ NewExtension->DeviceId = PciData->DeviceID;
+ NewExtension->RevisionId = PciData->RevisionID;
+ NewExtension->ProgIf = PciData->ProgIf;
+ NewExtension->SubClass = PciData->SubClass;
+ NewExtension->BaseClass = PciData->BaseClass;
+ NewExtension->HeaderType = PCI_CONFIGURATION_TYPE(PciData);
+
+ /* Check for modern bridge types, which are managed by the driver */
+ if ((NewExtension->BaseClass == PCI_CLASS_BRIDGE_DEV) &&
+ ((NewExtension->SubClass == PCI_SUBCLASS_BR_PCI_TO_PCI) ||
+ (NewExtension->SubClass == PCI_SUBCLASS_BR_CARDBUS)))
+ {
+ /* Acquire this device's lock */
+ KeEnterCriticalRegion();
+ KeWaitForSingleObject(&DeviceExtension->ChildListLock,
+ Executive,
+ KernelMode,
+ FALSE,
+ NULL);
+
+ /* Scan the bridge list until the first free entry */
+ for (BridgeExtension = &DeviceExtension->ChildBridgePdoList;
+ *BridgeExtension;
+ BridgeExtension = &(*BridgeExtension)->NextBridge);
+
+ /* Add this PDO as a bridge */
+ *BridgeExtension = NewExtension;
+ ASSERT(NewExtension->NextBridge == NULL);
+
+ /* Release this device's lock */
+ KeSetEvent(&DeviceExtension->ChildListLock,
+ IO_NO_INCREMENT,
+ FALSE);
+ KeLeaveCriticalRegion();
+ }
+
+ /* Get the PCI BIOS configuration saved in the registry */
+ Status = PciGetBiosConfig(NewExtension, BiosData);
+ if (NT_SUCCESS(Status))
+ {
+ /* This path has not yet been fully tested by eVb */
+ DPRINT1("Have BIOS configuration!\n");
+ UNIMPLEMENTED;
+
+ /* Check if the PCI BIOS configuration has changed */
+ if (!PcipIsSameDevice(NewExtension, BiosData))
+ {
+ /* This is considered failure, and new data will be saved */
+ Status = STATUS_UNSUCCESSFUL;
+ }
+ else
+ {
+ /* Data is still correct, check for interrupt line change */
+ if (BiosData->u.type0.InterruptLine !=
+ PciData->u.type0.InterruptLine)
+ {
+ /* Update the current BIOS with the saved interrupt line */
+ PciWriteDeviceConfig(NewExtension,
+ &BiosData->u.type0.InterruptLine,
+ FIELD_OFFSET(PCI_COMMON_HEADER,
+ u.type0.InterruptLine),
+ sizeof(UCHAR));
+ }
+
+ /* Save the BIOS interrupt line and the initial command */
+ NewExtension->RawInterruptLine = BiosData->u.type0.InterruptLine;
+ NewExtension->InitialCommand = BiosData->Command;
+ }
+ }
+
+ /* Check if no saved data was present or if it was a mismatch */
+ if (!NT_SUCCESS(Status))
+ {
+ /* Save the new data */
+ Status = PciSaveBiosConfig(NewExtension, PciData);
+ ASSERT(NT_SUCCESS(Status));
+
+ /* Save the interrupt line and command from the device */
+ NewExtension->RawInterruptLine = PciData->u.type0.InterruptLine;
+ NewExtension->InitialCommand = PciData->Command;
+ }
+
+ /* Save original command from the device and hack flags */
+ NewExtension->CommandEnables = PciData->Command;
+ NewExtension->HackFlags = HackFlags;
+
+ /* Get power, AGP, and other capability data */
+ PciGetEnhancedCapabilities(NewExtension, PciData);
+
+ /* Now configure the BARs */
+ Status = PciGetFunctionLimits(NewExtension, PciData, HackFlags);
+
+ /* Power up the device */
+ PciSetPowerManagedDevicePowerState(NewExtension, PowerDeviceD0, FALSE);
+
+ /* Apply any device hacks required for enumeration */
+ PciApplyHacks(DeviceExtension,
+ PciData,
+ PciSlot,
+ PCI_HACK_FIXUP_AFTER_CONFIGURATION,
+ NewExtension);
+
+ /* Save interrupt pin */
+ NewExtension->InterruptPin = PciData->u.type0.InterruptPin;
+
+ /*
+ * Use either this device's actual IRQ line or, if it's connected on
+ * a master bus whose IRQ line is actually connected to the host, use
+ * the HAL to query the bus' IRQ line and store that as the adjusted
+ * interrupt line instead
+ */
+ NewExtension->AdjustedInterruptLine = PciGetAdjustedInterruptLine(NewExtension);
+
+ /* Check if this device is used for PCI debugger cards */
+ NewExtension->OnDebugPath = PciIsDeviceOnDebugPath(NewExtension);
+
+ /* Check for devices with invalid/bogus subsystem data */
+ if (HackFlags & PCI_HACK_NO_SUBSYSTEM)
+ {
+ /* Set the subsystem information to zero instead */
+ NewExtension->SubsystemVendorId = 0;
+ NewExtension->SubsystemId = 0;
+ }
+
+ /* Scan all capabilities */
+ CapOffset = NewExtension->CapabilitiesPtr;
+ while (CapOffset)
+ {
+ /* Read this header */
+ TempOffset = PciReadDeviceCapability(NewExtension,
+ CapOffset,
+ 0,
+ &CapHeader,
+ sizeof(PCI_CAPABILITIES_HEADER));
+ if (TempOffset != CapOffset)
+ {
+ /* This is a strange issue that shouldn't happen normally */
+ DPRINT1("PCI - Failed to read PCI capability at offset 0x%02x\n",
+ CapOffset);
+ ASSERT(TempOffset == CapOffset);
+ }
+
+ /* Check for capabilities that this driver cares about */
+ switch (CapHeader.CapabilityID)
+ {
+ /* Power management capability is heavily used by the bus */
+ case PCI_CAPABILITY_ID_POWER_MANAGEMENT:
+
+ /* Dump the capability */
+ Name = "POWER";
+ Size = sizeof(PCI_PM_CAPABILITY);
+ break;
+
+ /* AGP capability is required for AGP bus functionality */
+ case PCI_CAPABILITY_ID_AGP:
+
+ /* Dump the capability */
+ Name = "AGP";
+ Size = sizeof(PCI_AGP_CAPABILITY);
+ break;
+
+ /* This driver doesn't really use anything other than that */
+ default:
+
+ /* Windows prints this, we could do a translation later */
+ Name = "UNKNOWN CAPABILITY";
+ Size = 0;
+ break;
+ }
+
+ /* Check if this is a capability that should be dumped */
+ if (Size)
+ {
+ /* Read the whole capability data */
+ TempOffset = PciReadDeviceCapability(NewExtension,
+ CapOffset,
+ CapHeader.CapabilityID,
+ &CapHeader,
+ Size);
+
+ if (TempOffset != CapOffset)
+ {
+ /* Again, a strange issue that shouldn't be seen */
+ DPRINT1("- Failed to read capability data. ***\n");
+ ASSERT(TempOffset == CapOffset);
+ }
+ }
+
+ /* Dump this capability */
+ DPRINT1("CAP @%02x ID %02x (%s)\n",
+ CapOffset, CapHeader.CapabilityID, Name);
+ for (i = 0; i < Size; i += 2)
+ DPRINT1(" %04x\n", *(PUSHORT)((ULONG_PTR)&CapHeader + i));
+ DPRINT1("\n");
+
+ /* Check the next capability */
+ CapOffset = CapHeader.Next;
+ }
+
+ /* Check for IDE controllers */
+ if ((NewExtension->BaseClass == PCI_CLASS_MASS_STORAGE_CTLR) &&
+ (NewExtension->SubClass == PCI_SUBCLASS_MSC_IDE_CTLR))
+ {
+ /* Do not allow them to power down completely */
+ NewExtension->DisablePowerDown = TRUE;
+ }
+
+ /*
+ * Check if this is a legacy bridge. Note that the i82375 PCI/EISA
+ * bridge that is present on certain NT Alpha machines appears as
+ * non-classified so detect it manually by scanning for its VID/PID.
+ */
+ if (((NewExtension->BaseClass == PCI_CLASS_BRIDGE_DEV) &&
+ ((NewExtension->SubClass == PCI_SUBCLASS_BR_ISA) ||
+ (NewExtension->SubClass == PCI_SUBCLASS_BR_EISA) ||
+ (NewExtension->SubClass == PCI_SUBCLASS_BR_MCA))) ||
+ ((NewExtension->VendorId == 0x8086) &&
+ (NewExtension->DeviceId == 0x482)))
+ {
+ /* Do not allow these legacy bridges to be powered down */
+ NewExtension->DisablePowerDown = TRUE;
+ }
+
+ /* Check if the BIOS did not configure a cache line size */
+ if (!PciData->CacheLineSize)
+ {
+ /* Check if the device is disabled */
+ if (!(NewExtension->CommandEnables & (PCI_ENABLE_IO_SPACE |
+ PCI_ENABLE_MEMORY_SPACE |
+ PCI_ENABLE_BUS_MASTER)))
+ {
+ /* Check if this is a PCI-X device*/
+ TempOffset = PciReadDeviceCapability(NewExtension,
+ NewExtension->CapabilitiesPtr,
+ PCI_CAPABILITY_ID_PCIX,
+ &PcixCapHeader,
+ sizeof(PCI_CAPABILITIES_HEADER));
+
+ /*
+ * A device with default cache line size and latency timer
+ * settings is considered to be unconfigured. Note that on
+ * PCI-X, the reset value of the latency timer field in the
+ * header is 64, not 0, hence why the check for PCI-X caps
+ * was required, and the value used here below.
+ */
+ if (!(PciData->LatencyTimer) ||
+ ((TempOffset) && (PciData->LatencyTimer == 64)))
+ {
+ /* Keep track of the fact that it needs configuration */
+ DPRINT1("PCI - ScanBus, PDOx %p found unconfigured\n",
+ NewExtension);
+ NewExtension->NeedsHotPlugConfiguration = TRUE;
+ }
+ }
+ }
+
+ /* Save latency and cache size information */
+ NewExtension->SavedLatencyTimer = PciData->LatencyTimer;
+ NewExtension->SavedCacheLineSize = PciData->CacheLineSize;
+
+ /* The PDO is now ready to go */
+ DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
+ }
+ }
+
+ /* Enumeration completed, do a final pass now that all devices are found */
+ if (ProcessFlag) PciProcessBus(DeviceExtension);
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS
+NTAPI
+PciQueryDeviceRelations(IN PPCI_FDO_EXTENSION DeviceExtension,
+ IN OUT PDEVICE_RELATIONS *pDeviceRelations)
+{
+ NTSTATUS Status;
+ PPCI_PDO_EXTENSION PdoExtension;
+ ULONG PdoCount = 0;
+ PDEVICE_RELATIONS DeviceRelations, NewRelations;
+ SIZE_T Size;
+ PDEVICE_OBJECT DeviceObject, *ObjectArray;
+ PAGED_CODE();
+
+ /* Make sure the FDO is started */
+ ASSERT(DeviceExtension->DeviceState == PciStarted);
+
+ /* Synchronize while we enumerate the bus */
+ Status = PciBeginStateTransition(DeviceExtension, PciSynchronizedOperation);
+ if (!NT_SUCCESS(Status)) return Status;
+
+ /* Scan all children PDO */
+ for (PdoExtension = DeviceExtension->ChildPdoList;
+ PdoExtension;
+ PdoExtension = PdoExtension->Next)
+ {
+ /* Invalidate them */
+ PdoExtension->NotPresent = TRUE;
+ }
+
+ /* Scan the PCI Bus */
+ Status = PciScanBus(DeviceExtension);
+ ASSERT(NT_SUCCESS(Status));
+
+ /* Enumerate all children PDO again */
+ for (PdoExtension = DeviceExtension->ChildPdoList;
+ PdoExtension;
+ PdoExtension = PdoExtension->Next)
+ {
+ /* Check for PDOs that are still invalidated */
+ if (PdoExtension->NotPresent)
+ {
+ /* This means this PDO existed before, but not anymore */
+ PdoExtension->ReportedMissing = TRUE;
+ DPRINT1("PCI - Old device (pdox) %p not found on rescan.\n",
+ PdoExtension);
+ }
+ else
+ {
+ /* Increase count of detected PDOs */
+ PdoCount++;
+ }
+ }
+
+ /* Read the current relations and add the newly discovered relations */
+ DeviceRelations = *pDeviceRelations;
+ Size = FIELD_OFFSET(DEVICE_RELATIONS, Objects) +
+ PdoCount * sizeof(PDEVICE_OBJECT);
+ if (DeviceRelations) Size += sizeof(PDEVICE_OBJECT) * DeviceRelations->Count;
+
+ /* Allocate the device relations */
+ NewRelations = (PDEVICE_RELATIONS)ExAllocatePoolWithTag(0, Size, 'BicP');
+ if (!NewRelations)
+ {
+ /* Out of space, cancel the operation */
+ PciCancelStateTransition(DeviceExtension, PciSynchronizedOperation);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ /* Check if there were any older relations */
+ NewRelations->Count = 0;
+ if (DeviceRelations)
+ {
+ /* Copy the old relations into the new buffer, then free the old one */
+ RtlCopyMemory(NewRelations,
+ DeviceRelations,
+ FIELD_OFFSET(DEVICE_RELATIONS, Objects) +
+ DeviceRelations->Count * sizeof(PDEVICE_OBJECT));
+ ExFreePoolWithTag(DeviceRelations, 0);
+ }
+
+ /* Print out that we're ready to dump relations */
+ DPRINT1("PCI QueryDeviceRelations/BusRelations FDOx %p (bus 0x%02x)\n",
+ DeviceExtension,
+ DeviceExtension->BaseBus);
+
+ /* Loop the current PDO children and the device relation object array */
+ PdoExtension = DeviceExtension->ChildPdoList;
+ ObjectArray = &NewRelations->Objects[NewRelations->Count];
+ while (PdoExtension)
+ {
+ /* Dump this relation */
+ DPRINT1(" QDR PDO %p (x %p)%s\n",
+ PdoExtension->PhysicalDeviceObject,
+ PdoExtension,
+ PdoExtension->NotPresent ?
+ "<Omitted, device flaged not present>" : "");
+
+ /* Is this PDO present? */
+ if (!PdoExtension->NotPresent)
+ {
+ /* Reference it and add it to the array */
+ DeviceObject = PdoExtension->PhysicalDeviceObject;
+ ObReferenceObject(DeviceObject);
+ *ObjectArray++ = DeviceObject;
+ }
+
+ /* Go to the next PDO */
+ PdoExtension = PdoExtension->Next;
+ }
+
+ /* Terminate dumping the relations */
+ DPRINT1(" QDR Total PDO count = %u (%u already in list)\n",
+ NewRelations->Count + PdoCount,
+ NewRelations->Count);
+
+ /* Return the final count and the new buffer */
+ NewRelations->Count += PdoCount;
+ *pDeviceRelations = NewRelations;
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS
+NTAPI
+PciSetResources(IN PPCI_PDO_EXTENSION PdoExtension,
+ IN BOOLEAN DoReset,
+ IN BOOLEAN SomethingSomethingDarkSide)
+{
+ PPCI_FDO_EXTENSION FdoExtension;
+ UCHAR NewCacheLineSize, NewLatencyTimer;
+ PCI_COMMON_HEADER PciData;
+ BOOLEAN Native;
+ PPCI_CONFIGURATOR Configurator;
+
+ UNREFERENCED_PARAMETER(SomethingSomethingDarkSide);
+
+ /* Get the FDO and read the configuration data */
+ FdoExtension = PdoExtension->ParentFdoExtension;
+ PciReadDeviceConfig(PdoExtension, &PciData, 0, PCI_COMMON_HDR_LENGTH);
+
+ /* Make sure this is still the same device */
+ if (!PcipIsSameDevice(PdoExtension, &PciData))
+ {
+ /* Fail */
+ ASSERTMSG("PCI Set resources - not same device", FALSE);
+ return STATUS_DEVICE_DOES_NOT_EXIST;
+ }
+
+ /* Nothing to set for a host bridge */
+ if ((PdoExtension->BaseClass == PCI_CLASS_BRIDGE_DEV) &&
+ (PdoExtension->SubClass == PCI_SUBCLASS_BR_HOST))
+ {
+ /* Fake success */
+ return STATUS_SUCCESS;
+ }
+
+ /* Check if an IDE controller is being reset */
+ if ((DoReset) &&
+ (PdoExtension->BaseClass == PCI_CLASS_MASS_STORAGE_CTLR) &&
+ (PdoExtension->SubClass == PCI_SUBCLASS_MSC_IDE_CTLR))
+ {
+ /* Turn off native mode */
+ Native = PciConfigureIdeController(PdoExtension, &PciData, FALSE);
+ ASSERT(Native == PdoExtension->IDEInNativeMode);
+ }
+
+ /* Check for update of a hotplug device, or first configuration of one */
+ if ((PdoExtension->NeedsHotPlugConfiguration) &&
+ (FdoExtension->HotPlugParameters.Acquired))
+ {
+ /* Don't have hotplug devices to test with yet, QEMU 0.14 should */
+ UNIMPLEMENTED_DBGBREAK();
+ }
+
+ /* Locate the correct resource configurator for this type of device */
+ Configurator = &PciConfigurators[PdoExtension->HeaderType];
+
+ /* Apply the settings change */
+ Configurator->ChangeResourceSettings(PdoExtension, &PciData);
+
+ /* Assume no update needed */
+ PdoExtension->UpdateHardware = FALSE;
+
+ /* Check if a reset is needed */
+ if (DoReset)
+ {
+ /* Reset resources */
+ Configurator->ResetDevice(PdoExtension, &PciData);
+ PciData.u.type0.InterruptLine = PdoExtension->RawInterruptLine;
+ }
+
+ /* Check if the latency timer changed */
+ NewLatencyTimer = PdoExtension->SavedLatencyTimer;
+ if (PciData.LatencyTimer != NewLatencyTimer)
+ {
+ /* Debug notification */
+ DPRINT1("PCI (pdox %p) changing latency from %02x to %02x.\n",
+ PdoExtension,
+ PciData.LatencyTimer,
+ NewLatencyTimer);
+ }
+
+ /* Check if the cache line changed */
+ NewCacheLineSize = PdoExtension->SavedCacheLineSize;
+ if (PciData.CacheLineSize != NewCacheLineSize)
+ {
+ /* Debug notification */
+ DPRINT1("PCI (pdox %p) changing cache line size from %02x to %02x.\n",
+ PdoExtension,
+ PciData.CacheLineSize,
+ NewCacheLineSize);
+ }
+
+ /* Inherit data from PDO extension */
+ PciData.LatencyTimer = PdoExtension->SavedLatencyTimer;
+ PciData.CacheLineSize = PdoExtension->SavedCacheLineSize;
+ PciData.u.type0.InterruptLine = PdoExtension->RawInterruptLine;
+
+ /* Apply any resource hacks required */
+ PciApplyHacks(FdoExtension,
+ &PciData,
+ PdoExtension->Slot,
+ PCI_HACK_FIXUP_BEFORE_UPDATE,
+ PdoExtension);
+
+ /* Check if I/O space was disabled by administrator or driver */
+ if (PdoExtension->IoSpaceNotRequired)
+ {
+ /* Don't turn on the decode */
+ PdoExtension->CommandEnables &= ~PCI_ENABLE_IO_SPACE;
+ }
+
+ /* Update the device with the new settings */
+ PciUpdateHardware(PdoExtension, &PciData);
+
+ /* Update complete */
+ PdoExtension->RawInterruptLine = PciData.u.type0.InterruptLine;
+ PdoExtension->NeedsHotPlugConfiguration = FALSE;
+ return STATUS_SUCCESS;
+}
+
/* EOF */