mirror of
https://github.com/reactos/reactos.git
synced 2024-12-28 18:15:11 +00:00
2272 lines
88 KiB
C
2272 lines
88 KiB
C
/*
|
|
* PROJECT: ReactOS PCI Bus Driver
|
|
* LICENSE: BSD - See COPYING.ARM in the top level directory
|
|
* FILE: drivers/bus/pci/enum.c
|
|
* PURPOSE: PCI Bus/Device Enumeration
|
|
* PROGRAMMERS: ReactOS Portable Systems Group
|
|
*/
|
|
|
|
/* INCLUDES *******************************************************************/
|
|
|
|
#include <pci.h>
|
|
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
/* 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 = CmiGetNextPartialDescriptor(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 resource 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 suffice 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 settings */
|
|
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 specify 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.\n", 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.\n", 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 */
|