/*++ Copyright (c) 2012 Minoca Corp. All Rights Reserved Module Name: pci.c Abstract: This module implements the PCI driver. Author: Evan Green 16-Sep-2012 Environment: Kernel --*/ // // ------------------------------------------------------------------- Includes // #include "pci.h" // // ---------------------------------------------------------------- Definitions // // // ------------------------------------------------------ Data Type Definitions // // // ----------------------------------------------- Internal Function Prototypes // KSTATUS PciAddDevice ( PVOID Driver, PSTR DeviceId, PSTR ClassId, PSTR CompatibleIds, PVOID DeviceToken ); VOID PciDispatchStateChange ( PIRP Irp, PVOID DeviceContext, PVOID IrpContext ); VOID PciDispatchSystemControl ( PIRP Irp, PVOID DeviceContext, PVOID IrpContext ); KSTATUS PcipReportChildren ( PIRP QueryChildrenIrp, PPCI_DEVICE PciDevice ); VOID PcipEnumerateChildren ( PDEVICE Device, PPCI_DEVICE PciDevice ); KSTATUS PcipQueryResourceRequirements ( PDEVICE Device, PPCI_DEVICE DeviceObject, PIRP Irp ); KSTATUS PcipQueryBridgeResourceRequirements ( PDEVICE Device, PPCI_DEVICE DeviceObject, PIRP Irp ); KSTATUS PcipSetDeviceResources ( PPCI_DEVICE DeviceContext, PRESOURCE_ALLOCATION_LIST AllocationList ); VOID PcipEnableDevice ( PPCI_DEVICE DeviceContext ); KSTATUS PcipSetBridgeDeviceResources ( PPCI_DEVICE DeviceContext, PRESOURCE_ALLOCATION_LIST AllocationList ); ULONG PcipFindDevice ( PPCI_DEVICE ParentBus, UCHAR Device, UCHAR Function ); ULONG PcipGetNewChildIndex ( PPCI_DEVICE ParentBus ); KSTATUS PcipQueryInterface ( PIRP Irp, PPCI_DEVICE PciDevice ); KSTATUS PcipInterfaceReadConfigSpace ( PVOID DeviceToken, ULONG Offset, ULONG AccessSize, PULONGLONG Value ); KSTATUS PcipInterfaceWriteConfigSpace ( PVOID DeviceToken, ULONG Offset, ULONG AccessSize, ULONGLONG Value ); KSTATUS PcipInterfaceReadSpecificConfigSpace ( PVOID DeviceToken, ULONG BusNumber, ULONG DeviceNumber, ULONG FunctionNumber, ULONG Offset, ULONG AccessSize, PULONGLONG Value ); KSTATUS PcipInterfaceWriteSpecificConfigSpace ( PVOID DeviceToken, ULONG BusNumber, ULONG DeviceNumber, ULONG FunctionNumber, ULONG Offset, ULONG AccessSize, ULONGLONG Value ); KSTATUS PcipStartBusDevice ( PIRP StartIrp, PPCI_DEVICE DeviceContext ); KSTATUS PcipCreateFunctionInterfaces ( PDEVICE Device, PPCI_DEVICE PciDevice ); KSTATUS PcipCreateBusInterfaces ( PDEVICE Device, PPCI_DEVICE PciDevice ); PSTR PcipGetClassId ( ULONG ClassCode ); KSTATUS PcipGetBusDriverDevice ( PDEVICE OsDevice, PPCI_DEVICE *BusDriverDevice ); // // -------------------------------------------------------------------- Globals // PDRIVER PciDriver = NULL; // // Store the UUID of PCI configuration space access. // UUID PciConfigSpaceUuid = UUID_PCI_CONFIG_ACCESS; // // Store the UUID of specific PCI configuration space access. // UUID PciSpecificConfigSpaceUuid = UUID_PCI_CONFIG_ACCESS_SPECIFIC; // // Store the UUID of the PCI MSI and MSI-X access. // UUID PciMessageSignaledInterruptsUuid = UUID_PCI_MESSAGE_SIGNALED_INTERRUPTS; // // Store the UUID of the ACPI bus number interface. // UUID PciAcpiBusAddressUuid = UUID_ACPI_BUS_ADDRESS; // // Store the UUID of the internal PCI interface for getting the bus driver's // PCI device structure. // UUID PciBusDriverDeviceUuid = {{0x73696D6F, 0x74207365, 0x656B206F, 0x61207066}}; // // ------------------------------------------------------------------ Functions // KSTATUS DriverEntry ( PDRIVER Driver ) /*++ Routine Description: This routine is the entry point for the PCI driver. It registers its other dispatch functions, and performs driver-wide initialization. Arguments: Driver - Supplies a pointer to the driver object. Return Value: STATUS_SUCCESS on success. Failure code on error. --*/ { DRIVER_FUNCTION_TABLE FunctionTable; KSTATUS Status; PciDriver = Driver; RtlZeroMemory(&FunctionTable, sizeof(DRIVER_FUNCTION_TABLE)); FunctionTable.Version = DRIVER_FUNCTION_TABLE_VERSION; FunctionTable.AddDevice = PciAddDevice; FunctionTable.DispatchStateChange = PciDispatchStateChange; FunctionTable.DispatchSystemControl = PciDispatchSystemControl; Status = IoRegisterDriverFunctions(Driver, &FunctionTable); return Status; } KSTATUS PciAddDevice ( PVOID Driver, PSTR DeviceId, PSTR ClassId, PSTR CompatibleIds, PVOID DeviceToken ) /*++ Routine Description: This routine is called when a PCI device is detected. PCI will attach itself to the driver stack. Arguments: Driver - Supplies a pointer to the driver being called. DeviceId - Supplies a pointer to a string with the device ID. ClassId - Supplies a pointer to a string containing the device's class ID. CompatibleIds - Supplies a pointer to a string containing device IDs that would be compatible with this device. DeviceToken - Supplies an opaque token that the driver can use to identify the device in the system. This token should be used when attaching to the stack. Return Value: STATUS_SUCCESS on success. STATUS_UNKNOWN_DEVICE if the device is not recognized by the driver. This usually a sign of misconfiguration (assigning PCI as a driver for something it does not own). Failure code if the driver was unsuccessful in attaching itself. --*/ { PPCI_DEVICE Device; PCI_DEVICE_TYPE DeviceType; BOOL Match; KSTATUS Status; Device = NULL; DeviceType = PciDeviceInvalid; Status = STATUS_UNKNOWN_DEVICE; // // The PCI driver is the functional driver for the PCI Root device. // Match = IoAreDeviceIdsEqual(DeviceId, PCI_BUS_ID); if (Match == FALSE) { Match = IoAreDeviceIdsEqual(DeviceId, PCI_EXPRESS_BUS_ID); } if (Match != FALSE) { DeviceType = PciDeviceBus; } else { if ((Match == FALSE) && (ClassId != NULL)) { Match = RtlAreStringsEqual(ClassId, PCI_BRIDGE_CLASS_ID, sizeof(PCI_BRIDGE_CLASS_ID)); } if ((Match == FALSE) && (ClassId != NULL)) { Match = RtlAreStringsEqual(ClassId, PCI_SUBTRACTIVE_BRIDGE_CLASS_ID, sizeof(PCI_SUBTRACTIVE_BRIDGE_CLASS_ID)); } if (Match != FALSE) { DeviceType = PciDeviceBridge; } } // // If the device was not idenfified, then the system was misconfigured to // have PCI be the driver of some random device. // if (Match == FALSE) { Status = STATUS_UNKNOWN_DEVICE; goto AddDeviceEnd; } ASSERT(DeviceType != PciDeviceInvalid); Device = MmAllocateNonPagedPool(sizeof(PCI_DEVICE), PCI_ALLOCATION_TAG); if (Device == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto AddDeviceEnd; } RtlZeroMemory(Device, sizeof(PCI_DEVICE)); Device->Type = DeviceType; Device->BusNumber = 0; if (DeviceType == PciDeviceBus) { Device->ReadConfig = PcipRootReadConfig; Device->WriteConfig = PcipRootWriteConfig; } Status = IoAttachDriverToDevice(Driver, DeviceToken, Device); if (!KSUCCESS(Status)) { goto AddDeviceEnd; } AddDeviceEnd: if (!KSUCCESS(Status)) { if (Device != NULL) { MmFreeNonPagedPool(Device); } } return Status; } VOID PciDispatchStateChange ( PIRP Irp, PVOID DeviceContext, PVOID IrpContext ) /*++ Routine Description: This routine handles State Change IRPs. Arguments: Irp - Supplies a pointer to the I/O request packet. DeviceContext - Supplies the context pointer supplied by the driver when it attached itself to the driver stack. Presumably this pointer contains driver-specific device context. IrpContext - Supplies the context pointer supplied by the driver when the IRP was created. Return Value: None. --*/ { PPCI_DEVICE PciDevice; KSTATUS Status; ASSERT(Irp->MajorCode == IrpMajorStateChange); PciDevice = DeviceContext; // // The IRP is on its way down the stack. Do most processing here. // if (Irp->Direction == IrpDown) { Status = STATUS_NOT_SUPPORTED; switch (Irp->MinorCode) { // // If the device is a function (therefore PCI is acting as the bus // driver), then return the device's resources. // case IrpMinorQueryResources: if (PciDevice->Type == PciDeviceFunction) { if (PciDevice->DeviceIsBridge != FALSE) { Status = PcipQueryBridgeResourceRequirements(Irp->Device, PciDevice, Irp); } else { Status = PcipQueryResourceRequirements(Irp->Device, PciDevice, Irp); } IoCompleteIrp(PciDriver, Irp, Status); } break; // // Assume the device is already started. Expose the interface for // interacting with the device's PCI config space. // case IrpMinorStartDevice: if (PciDevice->Type == PciDeviceFunction) { // // Set the BARs and enable the device. // if (PciDevice->DeviceIsBridge != FALSE) { Status = PcipSetBridgeDeviceResources( DeviceContext, Irp->U.StartDevice.BusLocalResources); } else { Status = PcipSetDeviceResources( DeviceContext, Irp->U.StartDevice.BusLocalResources); } if (!KSUCCESS(Status)) { IoCompleteIrp(PciDriver, Irp, Status); break; } // // Enable decoding on the device. // if (PciDevice->DeviceIsBridge == FALSE) { PcipEnableDevice(DeviceContext); } // // As the bus driver of a function, PCI completes the IRP. // IoCompleteIrp(PciDriver, Irp, Status); } else if ((PciDevice->Type == PciDeviceBus) || (PciDevice->Type == PciDeviceBridge)) { Status = PcipStartBusDevice(Irp, PciDevice); if (!KSUCCESS(Status)) { IoCompleteIrp(PciDriver, Irp, Status); } Status = STATUS_SUCCESS; } break; // // Enumerate any children on the bus. // case IrpMinorQueryChildren: // // If the driver is acting as a bus driver for a function, there are // no children. Complete the IRP. // if (PciDevice->Type == PciDeviceFunction) { IoCompleteIrp(PciDriver, Irp, STATUS_SUCCESS); // // If PCI is acting as the functional driver, enumerate the // children, but don't complete the IRP. // } else { Status = PcipReportChildren(Irp, DeviceContext); } break; // // Process interface requests. // case IrpMinorQueryInterface: Status = PcipQueryInterface(Irp, DeviceContext); if (Status != STATUS_NO_INTERFACE) { IoCompleteIrp(PciDriver, Irp, Status); } break; case IrpMinorIdle: if (PciDevice->Type == PciDeviceFunction) { IoCompleteIrp(PciDriver, Irp, STATUS_SUCCESS); } break; case IrpMinorSuspend: if (PciDevice->Type == PciDeviceFunction) { IoCompleteIrp(PciDriver, Irp, STATUS_SUCCESS); } break; case IrpMinorResume: if (PciDevice->Type == PciDeviceFunction) { IoCompleteIrp(PciDriver, Irp, STATUS_SUCCESS); } break; // // If the IRP is unknown, don't touch it. // default: break; } } else { ASSERT(Irp->Direction == IrpUp); } return; } VOID PciDispatchSystemControl ( PIRP Irp, PVOID DeviceContext, PVOID IrpContext ) /*++ Routine Description: This routine handles System Control IRPs. Arguments: Irp - Supplies a pointer to the I/O request packet. DeviceContext - Supplies the context pointer supplied by the driver when it attached itself to the driver stack. Presumably this pointer contains driver-specific device context. IrpContext - Supplies the context pointer supplied by the driver when the IRP was created. Return Value: None. --*/ { ASSERT(Irp->MajorCode == IrpMajorSystemControl); if (Irp->Direction == IrpDown) { IoCompleteIrp(PciDriver, Irp, STATUS_NOT_SUPPORTED); } else { ASSERT(Irp->Direction == IrpUp); } return; } // // --------------------------------------------------------- Internal Functions // KSTATUS PcipReportChildren ( PIRP QueryChildrenIrp, PPCI_DEVICE PciDevice ) /*++ Routine Description: This routine responds to a Query Children IRP. Arguments: QueryChildrenIrp - Supplies a pointer to the I/O request packet. PciDevice - Supplies a pointer to the PCI device context this IRP relates to. Return Value: STATUS_SUCCESS on success. STATUS_INSUFFICIENT_RESOURCES if the array could not be allocated for the children. --*/ { PDEVICE *Children; // // If the device is not a bus, it has no children. // if ((PciDevice->Type != PciDeviceBus) && (PciDevice->Type != PciDeviceBridge)) { QueryChildrenIrp->U.QueryChildren.Children = NULL; QueryChildrenIrp->U.QueryChildren.ChildCount = 0; return STATUS_SUCCESS; } // // Scan the bus and pick up any changes. // PcipEnumerateChildren(QueryChildrenIrp->Device, PciDevice); if (PciDevice->ChildCount == 0) { QueryChildrenIrp->U.QueryChildren.Children = NULL; QueryChildrenIrp->U.QueryChildren.ChildCount = 0; return STATUS_SUCCESS; } // // Allocated paged pool for the array to return. // Children = MmAllocatePagedPool(sizeof(PDEVICE) * PciDevice->ChildCount, PCI_ALLOCATION_TAG); if (Children == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } RtlCopyMemory(Children, PciDevice->Children, PciDevice->ChildCount * sizeof(PDEVICE)); QueryChildrenIrp->U.QueryChildren.Children = Children; QueryChildrenIrp->U.QueryChildren.ChildCount = PciDevice->ChildCount; return STATUS_SUCCESS; } VOID PcipEnumerateChildren ( PDEVICE Device, PPCI_DEVICE PciDevice ) /*++ Routine Description: This routine scans the given PCI bus, enumerating any new children and removing any missing ones. Arguments: Device - Supplies a pointer to the device. PciDevice - Supplies a pointer to the PCI device context relating to this device. Return Value: None. --*/ { PPCI_CHILD Child; ULONG ChildIndex; ULONG ClassCode; PSTR ClassCodeString; ULONG DeviceId; UCHAR DeviceNumber; UCHAR Function; ULONG HeaderType; ULONG Id; UCHAR MaxFunction; CHAR NewDeviceId[PCI_DEVICE_ID_SIZE]; PPCI_DEVICE NewPciDevice; KSTATUS Status; ULONG VendorId; // // If the device is not a bus, it has no children. // if ((PciDevice->Type != PciDeviceBus) && (PciDevice->Type != PciDeviceBridge)) { return; } // // Scan through all functions and all devices on this bus. // for (DeviceNumber = 0; DeviceNumber < MAX_PCI_DEVICE; DeviceNumber += 1) { // // Read configuration space to get the vendor and device ID. // Id = (ULONG)PciDevice->ReadConfig(PciDevice->BusNumber, DeviceNumber, 0, PCI_ID_OFFSET, sizeof(ULONG)); DeviceId = (Id & PCI_DEVICE_ID_MASK) >> PCI_DEVICE_ID_SHIFT; VendorId = Id & PCI_VENDOR_ID_MASK; if ((VendorId == 0) || (VendorId == PCI_INVALID_VENDOR_ID)) { continue; } // // Determine the total number of functions that need to be scanned for // this device by looking at the header type's multi-function flag. // HeaderType = (ULONG)PciDevice->ReadConfig(PciDevice->BusNumber, DeviceNumber, 0, PCI_HEADER_TYPE_OFFSET, sizeof(ULONG)); HeaderType = (HeaderType & PCI_HEADER_TYPE_MASK) >> PCI_HEADER_TYPE_SHIFT; if ((HeaderType & PCI_HEADER_TYPE_FLAG_MULTIPLE_FUNCTIONS) != 0) { MaxFunction = MAX_PCI_FUNCTION; } else { MaxFunction = 0; } for (Function = 0; Function <= MaxFunction; Function += 1) { // // Read configuration space to get the vendor and device ID if it // has not already been read. // if (Function != 0) { Id = (ULONG)PciDevice->ReadConfig(PciDevice->BusNumber, DeviceNumber, Function, PCI_ID_OFFSET, sizeof(ULONG)); DeviceId = (Id & PCI_DEVICE_ID_MASK) >> PCI_DEVICE_ID_SHIFT; VendorId = Id & PCI_VENDOR_ID_MASK; } // // Attempt to find a previously enumerated child for this device // and function. // ChildIndex = PcipFindDevice(PciDevice, DeviceNumber, Function); // // If there was a device here and it seems to have disappeared, // free the device and swap it out for the last one. // if (ChildIndex != MAX_ULONG) { Child = PciDevice->ChildrenData[ChildIndex]; if ((VendorId == Child->VendorId) && (DeviceId == Child->DeviceId)) { continue; } // // Devices shouldn't just come and go like this. If they really // do, then completely remove the old device and add a new // different one in its place, rather than this bizarre // switcharoo. // ASSERT(FALSE); ASSERT(PciDevice->ChildCount != 0); PciDevice->Children[ChildIndex] = PciDevice->Children[PciDevice->ChildCount - 1]; MmFreePagedPool(PciDevice->ChildrenData[ChildIndex]); PciDevice->ChildrenData[ChildIndex] = PciDevice->ChildrenData[PciDevice->ChildCount - 1]; PciDevice->Children[PciDevice->ChildCount - 1] = NULL; PciDevice->ChildrenData[PciDevice->ChildCount - 1] = NULL; PciDevice->ChildCount -= 1; // // There was no child there before. // } else { // // If the vendor ID is invalid, skip this function. // if ((VendorId == 0) || (VendorId == PCI_INVALID_VENDOR_ID)) { continue; } // // There's a child now where there didn't used to be, kick out // a new device. Start by getting an index where the child will // go in the array. This also allocates the new child structure. // ChildIndex = PcipGetNewChildIndex(PciDevice); if (ChildIndex == MAX_ULONG) { continue; } // // Create the new device ID string. This only needs to be // temporary, which is why the buffer is stack allocated. // RtlPrintToString(NewDeviceId, PCI_DEVICE_ID_SIZE, CharacterEncodingDefault, PCI_DEVICE_ID_FORMAT, VendorId, DeviceId); // // Read the class code and create a string from it. // ClassCode = (ULONG)PciDevice->ReadConfig(PciDevice->BusNumber, DeviceNumber, Function, PCI_CLASS_CODE_OFFSET, sizeof(ULONG)); ClassCode &= PCI_CLASS_CODE_MASK; ClassCodeString = PcipGetClassId(ClassCode); // // Create the driver context for the new child. // NewPciDevice = MmAllocateNonPagedPool(sizeof(PCI_DEVICE), PCI_ALLOCATION_TAG); if (NewPciDevice == NULL) { continue; } RtlZeroMemory(NewPciDevice, sizeof(PCI_DEVICE)); NewPciDevice->Type = PciDeviceFunction; NewPciDevice->BusNumber = PciDevice->BusNumber; NewPciDevice->DeviceNumber = DeviceNumber; NewPciDevice->FunctionNumber = Function; NewPciDevice->ClassCode = ClassCode; if ((ClassCode == PCI_SUBTRACTIVE_BRIDGE_CLASS_CODE) || (ClassCode == PCI_BRIDGE_CLASS_CODE)) { NewPciDevice->DeviceIsBridge = TRUE; } NewPciDevice->ReadConfig = PciDevice->ReadConfig; NewPciDevice->WriteConfig = PciDevice->WriteConfig; Status = PcipGetBusDriverDevice(Device, &(NewPciDevice->Parent)); if (!KSUCCESS(Status)) { ASSERT(FALSE); MmFreeNonPagedPool(NewPciDevice); } // // Create the child device and fill out the accounting // structures. // Status = IoCreateDevice(PciDriver, NewPciDevice, Device, NewDeviceId, ClassCodeString, NULL, &(PciDevice->Children[ChildIndex])); if (!KSUCCESS(Status)) { MmFreeNonPagedPool(NewPciDevice); continue; } Child = PciDevice->ChildrenData[ChildIndex]; Child->DeviceNumber = DeviceNumber; Child->Function = Function; Child->VendorId = VendorId; Child->DeviceId = DeviceId; PciDevice->ChildCount += 1; PcipCreateFunctionInterfaces(PciDevice->Children[ChildIndex], NewPciDevice); } } } return; } KSTATUS PcipQueryResourceRequirements ( PDEVICE Device, PPCI_DEVICE DeviceObject, PIRP Irp ) /*++ Routine Description: This routine determines the resource requirements of the given device. Arguments: Device - Supplies a pointer to the device to query. DeviceObject - Supplies a pointer to the PCI information associated with the system device. Irp - Supplies a pointer to the query resources IRP. Return Value: Status code. --*/ { ULONGLONG AddressDecode; RESOURCE_ALLOCATION Allocation; ULONG BarIndex; ULONGLONG BarLength[PCI_BAR_COUNT]; ULONG BitNumber; PRESOURCE_ALLOCATION_LIST BootAllocations; UCHAR Bus; PRESOURCE_CONFIGURATION_LIST ConfigurationList; USHORT ControlRegister; UCHAR DeviceNumber; UCHAR Function; ULONG InterruptPin; ULONGLONG Maximum; UCHAR Offset; PPCI_READ_CONFIG ReadConfig; RESOURCE_REQUIREMENT Requirement; PRESOURCE_REQUIREMENT_LIST RequirementList; KSTATUS Status; ULONG Value; PPCI_WRITE_CONFIG WriteConfig; ASSERT((Irp->MajorCode == IrpMajorStateChange) && (Irp->MinorCode == IrpMinorQueryResources)); // // Bridges are not handled in this function. // ASSERT((DeviceObject->Type == PciDeviceFunction) && (DeviceObject->DeviceIsBridge == FALSE)); BootAllocations = NULL; Bus = DeviceObject->BusNumber; ConfigurationList = NULL; DeviceNumber = DeviceObject->DeviceNumber; Function = DeviceObject->FunctionNumber; ReadConfig = DeviceObject->ReadConfig; RequirementList = NULL; WriteConfig = DeviceObject->WriteConfig; // // If the BARs have not been read yet from boot, see if the BIOS has // this device enabled, and read the BARs if so. // if (DeviceObject->BarsRead == FALSE) { DeviceObject->BarsRead = TRUE; // // If decoding is enabled, read the BARs. // ControlRegister = (USHORT)ReadConfig(Bus, DeviceNumber, Function, PCI_CONTROL_OFFSET, sizeof(USHORT)); DeviceObject->BootControlRegister = ControlRegister; if (((ControlRegister & PCI_CONTROL_IO_DECODE_ENABLED) != 0) || ((ControlRegister & PCI_CONTROL_MEMORY_DECODE_ENABLED) != 0)) { for (BarIndex = 0; BarIndex < PCI_BAR_COUNT; BarIndex += 1) { Offset = PCI_BAR_OFFSET + (BarIndex * sizeof(ULONG)); Value = (ULONG)ReadConfig(Bus, DeviceNumber, Function, Offset, sizeof(ULONG)); DeviceObject->BootConfiguration.U.Bar32[BarIndex] = Value; } } InterruptPin = (USHORT)ReadConfig(Bus, DeviceNumber, Function, PCI_INTERRUPT_LINE_OFFSET, sizeof(USHORT)); InterruptPin = (UCHAR)(InterruptPin >> BITS_PER_BYTE); DeviceObject->InterruptPin = InterruptPin; if (DeviceObject->InterruptPin > 4) { ASSERT(FALSE); DeviceObject->InterruptPin = 0; } // // Disable all decoding in preparation for the BAR test. // WriteConfig(Bus, DeviceNumber, Function, PCI_CONTROL_OFFSET, sizeof(USHORT), 0); // // The technique to determine how many resources a device needs is to // write all ones to each BAR, and then read them back to see which ones // stick, and therefore which addresses the device decodes. Write all // ones here. // for (BarIndex = 0; BarIndex < PCI_BAR_COUNT; BarIndex += 1) { Offset = PCI_BAR_OFFSET + (BarIndex * sizeof(ULONG)); WriteConfig(Bus, DeviceNumber, Function, Offset, sizeof(ULONG), -1); } // // Now read them back. // for (BarIndex = 0; BarIndex < PCI_BAR_COUNT; BarIndex += 1) { Offset = PCI_BAR_OFFSET + (BarIndex * sizeof(ULONG)); Value = (ULONG)ReadConfig(Bus, DeviceNumber, Function, Offset, sizeof(ULONG)); if (Value != 0) { DeviceObject->BarCount = BarIndex + 1; } DeviceObject->AddressDecodeBits.U.Bar32[BarIndex] = Value; } // // For the safest feeling possible, restore the BARs and control // register to what it was before. // for (BarIndex = 0; BarIndex < PCI_BAR_COUNT; BarIndex += 1) { Offset = PCI_BAR_OFFSET + (BarIndex * sizeof(ULONG)); Value = DeviceObject->BootConfiguration.U.Bar32[BarIndex]; WriteConfig(Bus, DeviceNumber, Function, Offset, sizeof(ULONG), Value); } WriteConfig(Bus, DeviceNumber, Function, PCI_CONTROL_OFFSET, sizeof(USHORT), ControlRegister); } RtlZeroMemory(&Requirement, sizeof(RESOURCE_REQUIREMENT)); // // Create a new resource requirement list. // RequirementList = IoCreateResourceRequirementList(); if (RequirementList == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto QueryResourceRequirementsEnd; } RtlZeroMemory(BarLength, sizeof(BarLength)); // // Loop through the BARs to determine the resource requirements. // for (BarIndex = 0; BarIndex < DeviceObject->BarCount; BarIndex += 1) { Value = DeviceObject->AddressDecodeBits.U.Bar32[BarIndex]; // // Create an I/O or memory space requirement. // if ((Value & PCI_BAR_IO_SPACE) != 0) { Requirement.Type = ResourceTypeIoPort; Requirement.Flags |= RESOURCE_FLAG_NOT_SHAREABLE; // // The value has 1 bit set for everything it decodes. Start from the // high order bits and find the maximum that it does decode. // if ((Value & (~PCI_BAR_IO_FLAGS_MASK)) == 0) { Maximum = 0; AddressDecode = 0; } else { BitNumber = 31; Maximum = 1 << BitNumber; while ((Maximum & Value) == 0) { BitNumber -= 1; Maximum = 1 << BitNumber; } // // Back up a smidge, the loop went one too far. // BitNumber += 1; Maximum = 1ULL << BitNumber; // // To get the needed size, OR in the empty bits on the right // (so the whole thing eventually rolls over), then mask off // the flags bits so they would be zero. Negate the whole // thing, and add 1 to roll over to a power of 2 that // represents the required size. // AddressDecode = (~((Value | ~(Maximum - 1)) & ~PCI_BAR_IO_FLAGS_MASK)) + 1; } Requirement.Minimum = 0; Requirement.Maximum = Maximum; Requirement.Length = AddressDecode; Requirement.Alignment = AddressDecode; Requirement.Characteristics = 0; if ((Value & PCI_BAR_MEMORY_PREFETCHABLE) != 0) { Requirement.Characteristics |= MEMORY_CHARACTERISTIC_PREFETCHABLE; } BarLength[BarIndex] = AddressDecode; // // Create a memory space requirement. // } else { Requirement.Type = ResourceTypePhysicalAddressSpace; Requirement.Flags |= RESOURCE_FLAG_NOT_SHAREABLE; AddressDecode = Value & (~PCI_BAR_MEMORY_FLAGS_MASK); Requirement.Minimum = 0; // // Set the minimum and maximum based on the BAR limits. // switch (Value & PCI_BAR_MEMORY_SIZE_MASK) { case PCI_BAR_MEMORY_32_BIT: BitNumber = 31; break; case PCI_BAR_MEMORY_1MB: BitNumber = 20; break; case PCI_BAR_MEMORY_64_BIT: ASSERT((BarIndex & 0x1) == 0); AddressDecode = DeviceObject->AddressDecodeBits.U.Bar64[BarIndex / 2] & (~PCI_BAR_MEMORY_FLAGS_MASK); BitNumber = 63; break; default: ASSERT(FALSE); BitNumber = 0; break; } // // Just like the I/O bars above, find the unset bits on the left // to get the maximum address. // if ((AddressDecode & (~PCI_BAR_MEMORY_FLAGS_MASK)) == 0) { Maximum = 0; AddressDecode = 0; } else { Maximum = 1ULL << BitNumber; while ((Maximum & AddressDecode) == 0) { BitNumber -= 1; Maximum = 1ULL << BitNumber; } // // Back up a smidge, the loop went too far. // BitNumber += 1; Maximum = 1ULL << BitNumber; // // Get the size needed for this BAR. Again this is done by // ORing in the unsupported bits on the left masking out the // flags, negating the whole thing, and adding one. Remember // that the flags were masked out already above. // if (BitNumber == 64) { AddressDecode = ~AddressDecode + 1; Maximum = MAX_ULONGLONG; } else { AddressDecode = (~(AddressDecode | ~(Maximum - 1))) + 1; } } Requirement.Length = AddressDecode; Requirement.Alignment = AddressDecode; Requirement.Maximum = Maximum; Requirement.Characteristics = 0; BarLength[BarIndex] = AddressDecode; // // 64 bit BARs take up two of the regular size BARs, so advance // past the second one. // if ((Value & PCI_BAR_MEMORY_SIZE_MASK) == PCI_BAR_MEMORY_64_BIT) { BarIndex += 1; } } // // Create and add the requirement to the list. // Status = IoCreateAndAddResourceRequirement(&Requirement, RequirementList, NULL); if (!KSUCCESS(Status)) { goto QueryResourceRequirementsEnd; } } // // If the interrupt pin is not zero, then request an interrupt line // resource as well. By default PCI interrupts are level triggered active // low, and shareable. // InterruptPin = DeviceObject->InterruptPin; if (InterruptPin != 0) { Requirement.Type = ResourceTypeInterruptLine; Requirement.Flags &= ~RESOURCE_FLAG_NOT_SHAREABLE; Requirement.Length = 1; Requirement.Characteristics = INTERRUPT_LINE_ACTIVE_LOW; Requirement.Flags = 0; Requirement.Alignment = 1; Requirement.Minimum = InterruptPin; Requirement.Maximum = InterruptPin + 1; Status = IoCreateAndAddResourceRequirement(&Requirement, RequirementList, NULL); if (!KSUCCESS(Status)) { goto QueryResourceRequirementsEnd; } } // // Create the resource configuration list. // ConfigurationList = IoCreateResourceConfigurationList(RequirementList); if (ConfigurationList == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto QueryResourceRequirementsEnd; } RequirementList = NULL; // // Create the boot configuration. // BootAllocations = IoCreateResourceAllocationList(); if (BootAllocations == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto QueryResourceRequirementsEnd; } RtlZeroMemory(&Allocation, sizeof(RESOURCE_ALLOCATION)); for (BarIndex = 0; BarIndex < DeviceObject->BarCount; BarIndex += 1) { Value = DeviceObject->BootConfiguration.U.Bar32[BarIndex]; // // Create an I/O or memory space allocation. // if ((Value & PCI_BAR_IO_SPACE) != 0) { Allocation.Type = ResourceTypeIoPort; Allocation.Allocation = Value & (~PCI_BAR_IO_FLAGS_MASK); Allocation.Flags = RESOURCE_FLAG_NOT_SHAREABLE; Allocation.Length = 0; if ((DeviceObject->BootControlRegister & PCI_CONTROL_IO_DECODE_ENABLED) != 0) { Allocation.Length = BarLength[BarIndex]; } // // Create a memory space allocation. // } else { Allocation.Type = ResourceTypePhysicalAddressSpace; Allocation.Allocation = Value & (~PCI_BAR_MEMORY_FLAGS_MASK); Allocation.Flags = RESOURCE_FLAG_NOT_SHAREABLE; Allocation.Length = 0; if ((DeviceObject->BootControlRegister & PCI_CONTROL_MEMORY_DECODE_ENABLED) != 0) { Allocation.Length = BarLength[BarIndex]; if ((Value & PCI_BAR_MEMORY_SIZE_MASK) == PCI_BAR_MEMORY_64_BIT) { ASSERT((BarIndex & 0x1) == 0); Allocation.Allocation = DeviceObject->BootConfiguration.U.Bar64[BarIndex / 2] & (~PCI_BAR_MEMORY_FLAGS_MASK); BarIndex += 1; } } } // // Create and add the allocation to the list. // Status = IoCreateAndAddResourceAllocation(&Allocation, BootAllocations); if (!KSUCCESS(Status)) { goto QueryResourceRequirementsEnd; } } // // Add the interrupt pin to the boot configuration. // if (InterruptPin != 0) { Allocation.Type = ResourceTypeInterruptLine; Allocation.Allocation = InterruptPin; Allocation.Length = 1; Allocation.Flags = 0; Allocation.Characteristics = INTERRUPT_LINE_ACTIVE_LOW; Status = IoCreateAndAddResourceAllocation(&Allocation, BootAllocations); if (!KSUCCESS(Status)) { goto QueryResourceRequirementsEnd; } } Status = STATUS_SUCCESS; QueryResourceRequirementsEnd: if (!KSUCCESS(Status)) { if (RequirementList != NULL) { IoDestroyResourceRequirementList(RequirementList); } if (ConfigurationList != NULL) { IoDestroyResourceConfigurationList(ConfigurationList); ConfigurationList = NULL; } if (BootAllocations != NULL) { IoDestroyResourceAllocationList(BootAllocations); BootAllocations = NULL; } } Irp->U.QueryResources.ResourceRequirements = ConfigurationList; Irp->U.QueryResources.BootAllocation = BootAllocations; return Status; } KSTATUS PcipQueryBridgeResourceRequirements ( PDEVICE Device, PPCI_DEVICE DeviceObject, PIRP Irp ) /*++ Routine Description: This routine determines the resource requirements of the given PCI bridge. For the confused, this routine is called by PCI acting as the bus driver, not the function driver. Arguments: Device - Supplies a pointer to the device to query. DeviceObject - Supplies a pointer to the PCI information associated with the system device. Irp - Supplies a pointer to the query resources IRP. Return Value: Status code. --*/ { RESOURCE_ALLOCATION Allocation; BOOL BarsRead; PRESOURCE_ALLOCATION_LIST BootAllocations; UCHAR Bus; PRESOURCE_CONFIGURATION_LIST ConfigurationList; UCHAR DeviceNumber; UCHAR Function; UCHAR InterruptPin; ULONG IoDecodeBase; ULONG IoDecodeLimit; BOOL IoDecodeUpperBaseValid; BOOL IoDecodeUpperLimitValid; ULONG MemoryDecodeBase; ULONG MemoryDecodeLimit; ULONGLONG PrefetchableMemoryDecodeBase; ULONGLONG PrefetchableMemoryDecodeLimit; BOOL PrefetchableMemoryUpperBaseValid; BOOL PrefetchableMemoryUpperLimitValid; PPCI_READ_CONFIG ReadConfig; RESOURCE_REQUIREMENT Requirement; PRESOURCE_REQUIREMENT_LIST RequirementList; UCHAR SecondaryBusNumber; KSTATUS Status; ULONG Value; ULONG ValueHigh; ASSERT((Irp->MajorCode == IrpMajorStateChange) && (Irp->MinorCode == IrpMinorQueryResources)); // // Only bridges are handled in this function. // ASSERT((DeviceObject->Type == PciDeviceFunction) && (DeviceObject->DeviceIsBridge != FALSE)); BarsRead = FALSE; BootAllocations = NULL; Bus = DeviceObject->BusNumber; ConfigurationList = NULL; DeviceNumber = DeviceObject->DeviceNumber; Function = DeviceObject->FunctionNumber; IoDecodeBase = MAX_USHORT; IoDecodeLimit = 0; MemoryDecodeBase = MAX_ULONG; MemoryDecodeLimit = 0; PrefetchableMemoryDecodeBase = MAX_ULONGLONG; PrefetchableMemoryDecodeLimit = 0; ReadConfig = DeviceObject->ReadConfig; RequirementList = NULL; SecondaryBusNumber = 0xFF; // // If the BARs have not been read yet from boot, see if the BIOS has // this device enabled, and read the BARs if so. // if (DeviceObject->BarsRead == FALSE) { DeviceObject->BarsRead = TRUE; BarsRead = TRUE; // // Read the bus number BAR to see how the BIOS configured it. // Value = (ULONG)ReadConfig(Bus, DeviceNumber, Function, PCI_BRIDGE_BUS_NUMBERS_OFFSET, sizeof(ULONG)); SecondaryBusNumber = (UCHAR)(Value >> PCI_BRIDGE_SECONDARY_BUS_SHIFT); // // Read the value set by the BIOS for the I/O decode region. // IoDecodeUpperBaseValid = FALSE; IoDecodeUpperLimitValid = FALSE; Value = (USHORT)ReadConfig(Bus, DeviceNumber, Function, PCI_BRIDGE_IO_BAR_OFFSET, sizeof(USHORT)); if ((Value & PCI_BRIDGE_IO_BASE_DECODE_MASK) == PCI_BRIDGE_IO_BASE_DECODE_32_BIT) { IoDecodeUpperBaseValid = TRUE; } if ((Value & PCI_BRIDGE_IO_LIMIT_DECODE_MASK) == PCI_BRIDGE_IO_LIMIT_DECODE_32_BIT) { IoDecodeUpperLimitValid = TRUE; } IoDecodeBase = (Value & PCI_BRIDGE_IO_BASE_MASK) << PCI_BRIDGE_IO_BASE_ADDRESS_SHIFT; IoDecodeLimit = Value & PCI_BRIDGE_IO_LIMIT_MASK; if ((IoDecodeUpperBaseValid != FALSE) || (IoDecodeUpperLimitValid != FALSE)) { ValueHigh = (USHORT)ReadConfig(Bus, DeviceNumber, Function, PCI_BRIDGE_IO_HIGH_BAR_OFFSET, sizeof(ULONG)); if (IoDecodeUpperBaseValid != FALSE) { IoDecodeBase |= (ValueHigh & PCI_BRIDGE_IO_BASE_HIGH_MASK) << PCI_BRIDGE_IO_BASE_HIGH_ADDRESS_SHIFT; } if (IoDecodeUpperLimitValid != FALSE) { IoDecodeLimit |= ValueHigh & PCI_BRIDGE_IO_LIMIT_HIGH_MASK; } } // // Read the value set by the BIOS for the memory decode region. // Value = (ULONG)ReadConfig(Bus, DeviceNumber, Function, PCI_BRIDGE_MEMORY_BAR_OFFSET, sizeof(ULONG)); MemoryDecodeBase = (Value & PCI_BRIDGE_MEMORY_BASE_MASK) << PCI_BRIDGE_MEMORY_BASE_ADDRESS_SHIFT; MemoryDecodeLimit = Value & PCI_BRIDGE_MEMORY_LIMIT_MASK; // // Read the prefetchable memory range as well. // PrefetchableMemoryUpperBaseValid = FALSE; PrefetchableMemoryUpperLimitValid = FALSE; Value = (ULONG)ReadConfig(Bus, DeviceNumber, Function, PCI_BRIDGE_PREFETCHABLE_MEMORY_BAR_OFFSET, sizeof(ULONG)); if ((Value & PCI_BRIDGE_PREFETCHABLE_MEMORY_BASE_DECODE_MASK) == PCI_BRIDGE_PREFETCHABLE_MEMORY_BASE_DECODE_64_BIT) { PrefetchableMemoryUpperBaseValid = TRUE; } if ((Value & PCI_BRIDGE_PREFETCHABLE_MEMORY_LIMIT_DECODE_MASK) == PCI_BRIDGE_PREFETCHABLE_MEMORY_LIMIT_DECODE_64_BIT) { PrefetchableMemoryUpperLimitValid = TRUE; } PrefetchableMemoryDecodeBase = (Value & PCI_BRIDGE_PREFETCHABLE_MEMORY_BASE_MASK) << PCI_BRIDGE_PREFETCHABLE_MEMORY_BASE_ADDRESS_SHIFT; PrefetchableMemoryDecodeLimit = Value & PCI_BRIDGE_PREFETCHABLE_MEMORY_LIMIT_MASK; if (PrefetchableMemoryUpperBaseValid != FALSE) { ValueHigh = (ULONG)ReadConfig( Bus, DeviceNumber, Function, PCI_BRIDGE_PREFETCHABLE_MEMORY_BASE_HIGH_OFFSET, sizeof(ULONG)); PrefetchableMemoryDecodeBase |= (ULONGLONG)ValueHigh << PCI_BRIDGE_PREFETCHABLE_MEMORY_HIGH_ADDRESS_SHIFT; } if (PrefetchableMemoryUpperLimitValid != FALSE) { ValueHigh = (ULONG)ReadConfig( Bus, DeviceNumber, Function, PCI_BRIDGE_PREFETCHABLE_MEMORY_LIMIT_HIGH_OFFSET, sizeof(ULONG)); PrefetchableMemoryDecodeLimit |= (ULONGLONG)ValueHigh << PCI_BRIDGE_PREFETCHABLE_MEMORY_HIGH_ADDRESS_SHIFT; } InterruptPin = (USHORT)ReadConfig(Bus, DeviceNumber, Function, PCI_INTERRUPT_LINE_OFFSET, sizeof(USHORT)); InterruptPin = (UCHAR)(InterruptPin >> BITS_PER_BYTE); DeviceObject->InterruptPin = InterruptPin; if (DeviceObject->InterruptPin > 4) { ASSERT(FALSE); DeviceObject->InterruptPin = 0; } } RtlZeroMemory(&Requirement, sizeof(RESOURCE_REQUIREMENT)); // // Create a new resource requirement list and add the bus number // requirement. // RequirementList = IoCreateResourceRequirementList(); if (RequirementList == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto QueryBridgeResourceRequirementsEnd; } Requirement.Type = ResourceTypeBusNumber; Requirement.Minimum = 0; Requirement.Maximum = (UCHAR)-1; Requirement.Length = 1; Requirement.Characteristics = 0; Requirement.Alignment = 0; Requirement.Flags = RESOURCE_FLAG_NOT_SHAREABLE; Status = IoCreateAndAddResourceRequirement(&Requirement, RequirementList, NULL); if (!KSUCCESS(Status)) { goto QueryBridgeResourceRequirementsEnd; } // // Add empty requirements for the windows. // Requirement.Type = ResourceTypeIoPort; Requirement.Minimum = 0; Requirement.Maximum = MAX_ULONG; Requirement.Length = 0; Requirement.Characteristics = 0; Requirement.Alignment = PCI_BRIDGE_IO_GRANULARITY; Status = IoCreateAndAddResourceRequirement(&Requirement, RequirementList, NULL); if (!KSUCCESS(Status)) { goto QueryBridgeResourceRequirementsEnd; } Requirement.Type = ResourceTypePhysicalAddressSpace; Requirement.Minimum = 0; Requirement.Maximum = MAX_ULONG; Requirement.Length = 0; Requirement.Characteristics = 0; Requirement.Alignment = PCI_BRIDGE_MEMORY_GRANULARITY; Status = IoCreateAndAddResourceRequirement(&Requirement, RequirementList, NULL); if (!KSUCCESS(Status)) { goto QueryBridgeResourceRequirementsEnd; } // // The prefetchable memory window is the same as the MMIO region, but is // 64-bit capable. // Requirement.Maximum = MAX_ULONGLONG; Requirement.Characteristics = MEMORY_CHARACTERISTIC_PREFETCHABLE; Status = IoCreateAndAddResourceRequirement(&Requirement, RequirementList, NULL); if (!KSUCCESS(Status)) { goto QueryBridgeResourceRequirementsEnd; } // // Create the resource configuration list. // ConfigurationList = IoCreateResourceConfigurationList(RequirementList); if (ConfigurationList == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto QueryBridgeResourceRequirementsEnd; } RequirementList = NULL; // // Create the boot configuration. // BootAllocations = IoCreateResourceAllocationList(); if (BootAllocations == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto QueryBridgeResourceRequirementsEnd; } RtlZeroMemory(&Allocation, sizeof(RESOURCE_ALLOCATION)); if (BarsRead != FALSE) { if (SecondaryBusNumber != 0xFF) { Allocation.Type = ResourceTypeBusNumber; Allocation.Allocation = SecondaryBusNumber; Allocation.Length = 1; Allocation.Flags = RESOURCE_FLAG_NOT_SHAREABLE; Status = IoCreateAndAddResourceAllocation(&Allocation, BootAllocations); if (!KSUCCESS(Status)) { goto QueryBridgeResourceRequirementsEnd; } Allocation.Type = ResourceTypeIoPort; Allocation.Allocation = IoDecodeBase; if (IoDecodeLimit >= IoDecodeBase) { Allocation.Length = (IoDecodeLimit + PCI_BRIDGE_IO_GRANULARITY) - IoDecodeBase; } else { Allocation.Length = 0; } Allocation.Flags = RESOURCE_FLAG_NOT_SHAREABLE; Status = IoCreateAndAddResourceAllocation(&Allocation, BootAllocations); if (!KSUCCESS(Status)) { goto QueryBridgeResourceRequirementsEnd; } Allocation.Type = ResourceTypePhysicalAddressSpace; Allocation.Allocation = MemoryDecodeBase; if (MemoryDecodeLimit >= MemoryDecodeBase) { Allocation.Length = (MemoryDecodeLimit + PCI_BRIDGE_MEMORY_GRANULARITY) - MemoryDecodeBase; } else { Allocation.Length = 0; } Allocation.Flags = RESOURCE_FLAG_NOT_SHAREABLE; Status = IoCreateAndAddResourceAllocation(&Allocation, BootAllocations); if (!KSUCCESS(Status)) { goto QueryBridgeResourceRequirementsEnd; } Allocation.Type = ResourceTypePhysicalAddressSpace; Allocation.Allocation = PrefetchableMemoryDecodeBase; if (PrefetchableMemoryDecodeLimit >= PrefetchableMemoryDecodeBase) { Allocation.Length = (PrefetchableMemoryDecodeLimit + PCI_BRIDGE_MEMORY_GRANULARITY) - PrefetchableMemoryDecodeBase; } else { Allocation.Length = 0; } Allocation.Characteristics = MEMORY_CHARACTERISTIC_PREFETCHABLE; Allocation.Flags = RESOURCE_FLAG_NOT_SHAREABLE; Status = IoCreateAndAddResourceAllocation(&Allocation, BootAllocations); if (!KSUCCESS(Status)) { goto QueryBridgeResourceRequirementsEnd; } } } Status = STATUS_SUCCESS; QueryBridgeResourceRequirementsEnd: if (!KSUCCESS(Status)) { if (RequirementList != NULL) { IoDestroyResourceRequirementList(RequirementList); } if (ConfigurationList != NULL) { IoDestroyResourceConfigurationList(ConfigurationList); ConfigurationList = NULL; } if (BootAllocations != NULL) { IoDestroyResourceAllocationList(BootAllocations); BootAllocations = NULL; } } Irp->U.QueryResources.ResourceRequirements = ConfigurationList; Irp->U.QueryResources.BootAllocation = BootAllocations; return Status; } KSTATUS PcipSetDeviceResources ( PPCI_DEVICE DeviceContext, PRESOURCE_ALLOCATION_LIST AllocationList ) /*++ Routine Description: This routine sets the assigned resources in the PCI BARs. Arguments: DeviceContext - Supplies a pointer to the device to set. AllocationList - Supplies a pointer to the resource allocation list containing the device's resource assignment. Return Value: Status code. --*/ { ULONGLONG AddressDecode; PRESOURCE_ALLOCATION Allocation; ULONG BarIndex; ULONG BarSize; UCHAR Bus; USHORT ControlRegister; UCHAR DeviceNumber; UCHAR Function; PPCI_MSI_CONTEXT MsiContext; UCHAR Offset; ULONG PendingArrayIndex; ULONG PendingArrayOffset; PPCI_READ_CONFIG ReadConfig; RESOURCE_TYPE ResourceType; KSTATUS Status; ULONGLONG Value; ULONG VectorTableIndex; ULONG VectorTableOffset; PPCI_WRITE_CONFIG WriteConfig; // // This routine only handles functions, not bridges. // ASSERT((DeviceContext->Type == PciDeviceFunction) && (DeviceContext->DeviceIsBridge == FALSE)); if (AllocationList == NULL) { Status = STATUS_SUCCESS; goto SetDeviceResourcesEnd; } Bus = DeviceContext->BusNumber; DeviceNumber = DeviceContext->DeviceNumber; Function = DeviceContext->FunctionNumber; ReadConfig = DeviceContext->ReadConfig; WriteConfig = DeviceContext->WriteConfig; // // If MSI-X is available on the device then prepare to squirrel away the // physical address of the table and pending array. // MsiContext = DeviceContext->MsiContext; if ((MsiContext != NULL) && (MsiContext->MsiXOffset != 0)) { PcipGetMsiXBarInformation(DeviceContext, &VectorTableIndex, &VectorTableOffset, &PendingArrayIndex, &PendingArrayOffset); } // // Read the control register. // ControlRegister = (USHORT)ReadConfig(Bus, DeviceNumber, Function, PCI_CONTROL_OFFSET, sizeof(USHORT)); // // Disable all decoding in preparation for setting the BARs. // WriteConfig(Bus, DeviceNumber, Function, PCI_CONTROL_OFFSET, sizeof(USHORT), 0); // // Loop through the BARs and assign resources to each one. // Allocation = IoGetNextResourceAllocation(AllocationList, NULL); for (BarIndex = 0; BarIndex < DeviceContext->BarCount; BarIndex += 1) { AddressDecode = DeviceContext->AddressDecodeBits.U.Bar32[BarIndex]; // // Get the resource type for this BAR. // ResourceType = ResourceTypePhysicalAddressSpace; if ((AddressDecode & PCI_BAR_IO_SPACE) != 0) { ResourceType = ResourceTypeIoPort; } // // Find the next resource of that type. // while (Allocation->Type != ResourceType) { Allocation = IoGetNextResourceAllocation(AllocationList, Allocation); if (Allocation == NULL) { Status = STATUS_INVALID_CONFIGURATION; goto SetDeviceResourcesEnd; } } // // Skip it if it's zero length. // if (AddressDecode == 0) { ASSERT(Allocation->Length == 0); Allocation = IoGetNextResourceAllocation(AllocationList, Allocation); continue; } // // See if this is a 64 bit bar. // BarSize = sizeof(ULONG); if ((ResourceType == ResourceTypePhysicalAddressSpace) && ((AddressDecode & PCI_BAR_MEMORY_SIZE_MASK) == PCI_BAR_MEMORY_64_BIT)) { BarSize = sizeof(ULONGLONG); } Value = Allocation->Allocation; if (ResourceType == ResourceTypePhysicalAddressSpace) { ASSERT((Value & PCI_BAR_MEMORY_FLAGS_MASK) == 0); ControlRegister |= PCI_CONTROL_MEMORY_DECODE_ENABLED; } else { ASSERT(ResourceType == ResourceTypeIoPort); ASSERT((Value & PCI_BAR_IO_FLAGS_MASK) == 0); ControlRegister |= PCI_CONTROL_IO_DECODE_ENABLED; Value |= PCI_BAR_IO_SPACE; } // // Write out the BAR. // Offset = PCI_BAR_OFFSET + (BarIndex * sizeof(ULONG)); WriteConfig(Bus, DeviceNumber, Function, Offset, BarSize, Value); // // If MSI-X is available then check to see if this is the BAR for // either the vector table or pending bit array. They could be in the // same BAR. // if ((MsiContext != NULL) && (MsiContext->MsiXOffset != 0)) { if (VectorTableIndex == BarIndex) { ASSERT(MsiContext->MsiXTablePhysicalAddress == INVALID_PHYSICAL_ADDRESS); MsiContext->MsiXTablePhysicalAddress = Value + VectorTableOffset; } if (PendingArrayIndex == BarIndex) { ASSERT(MsiContext->MsiXPendingArrayPhysicalAddress == INVALID_PHYSICAL_ADDRESS); MsiContext->MsiXPendingArrayPhysicalAddress = Value + PendingArrayOffset; } } // // Skip over the next BAR if this one was a 64-bit BAR. // if ((ResourceType == ResourceTypePhysicalAddressSpace) && ((AddressDecode & PCI_BAR_MEMORY_SIZE_MASK) == PCI_BAR_MEMORY_64_BIT)) { BarIndex += 1; } // // Move on to the next allocation. // Allocation = IoGetNextResourceAllocation(AllocationList, Allocation); } // // Write out the control register to enable the device. // WriteConfig(Bus, DeviceNumber, Function, PCI_CONTROL_OFFSET, sizeof(USHORT), ControlRegister); Status = STATUS_SUCCESS; SetDeviceResourcesEnd: return Status; } VOID PcipEnableDevice ( PPCI_DEVICE DeviceContext ) /*++ Routine Description: This routine enables the I/O space, memory space, and Bus master bits in the PCI device. Arguments: DeviceContext - Supplies a pointer to the device to set. Return Value: None. --*/ { UCHAR Bus; USHORT CommandRegister; UCHAR DeviceNumber; UCHAR Function; PPCI_READ_CONFIG ReadConfig; PPCI_WRITE_CONFIG WriteConfig; // // This routine only handles functions, not bridges. // ASSERT((DeviceContext->Type == PciDeviceFunction) && (DeviceContext->DeviceIsBridge == FALSE)); Bus = DeviceContext->BusNumber; DeviceNumber = DeviceContext->DeviceNumber; Function = DeviceContext->FunctionNumber; ReadConfig = DeviceContext->ReadConfig; WriteConfig = DeviceContext->WriteConfig; // // Read the command register, and enable some bits. // CommandRegister = (USHORT)ReadConfig(Bus, DeviceNumber, Function, PCI_CONTROL_OFFSET, sizeof(USHORT)); CommandRegister |= PCI_CONTROL_IO_DECODE_ENABLED | PCI_CONTROL_MEMORY_DECODE_ENABLED | PCI_CONTROL_WRITE_INVALIDATE_ENABLED | PCI_CONTROL_BUS_MASTER_ENABLED; // // Disable all decoding in preparation for setting the BARs. // WriteConfig(Bus, DeviceNumber, Function, PCI_CONTROL_OFFSET, sizeof(USHORT), CommandRegister); return; } KSTATUS PcipSetBridgeDeviceResources ( PPCI_DEVICE DeviceContext, PRESOURCE_ALLOCATION_LIST AllocationList ) /*++ Routine Description: This routine sets the assigned resource window into the given bridge. Arguments: DeviceContext - Supplies a pointer to the device to set. AllocationList - Supplies a pointer to the resource allocation list containing the device's resource assignment. Return Value: Status code. --*/ { PRESOURCE_ALLOCATION Allocation; UCHAR Bus; ULONG BusRegister; USHORT ControlRegister; UCHAR DeviceNumber; UCHAR Function; ULONG IoPortHigh; USHORT IoPortRegister; ULONGLONG Limit; ULONG MemoryRegister; UCHAR OriginalSecondaryBusNumber; ULONG PrefetchableMemoryBaseHigh; ULONG PrefetchableMemoryLimitHigh; ULONG PrefetchableMemoryLow; UCHAR PrimaryBusNumber; PPCI_READ_CONFIG ReadConfig; UCHAR SecondaryBusNumber; KSTATUS Status; UCHAR SubordinateBusNumber; PPCI_WRITE_CONFIG WriteConfig; // // This routine only handles bridges. // ASSERT((DeviceContext->Type == PciDeviceFunction) && (DeviceContext->DeviceIsBridge != FALSE)); if (AllocationList == NULL) { Status = STATUS_SUCCESS; goto SetBridgeDeviceResourcesEnd; } // // Initialize the locals. Set the window registers up so that the base is // higher than the limit, a safe default if no resources were given for that // window. // Bus = DeviceContext->BusNumber; DeviceNumber = DeviceContext->DeviceNumber; Function = DeviceContext->FunctionNumber; IoPortRegister = ((MAX_USHORT >> PCI_BRIDGE_IO_BASE_ADDRESS_SHIFT) & PCI_BRIDGE_IO_BASE_MASK) | (0 & PCI_BRIDGE_IO_LIMIT_MASK); IoPortHigh = 0; MemoryRegister = ((MAX_ULONG >> PCI_BRIDGE_MEMORY_BASE_ADDRESS_SHIFT) & PCI_BRIDGE_MEMORY_BASE_MASK) | (0 & PCI_BRIDGE_MEMORY_LIMIT_MASK); PrimaryBusNumber = Bus; PrefetchableMemoryLow = ((MAX_ULONG >> PCI_BRIDGE_MEMORY_BASE_ADDRESS_SHIFT) & PCI_BRIDGE_MEMORY_BASE_MASK) | (0 & PCI_BRIDGE_MEMORY_LIMIT_MASK); PrefetchableMemoryBaseHigh = MAX_ULONG; PrefetchableMemoryLimitHigh = 0; SecondaryBusNumber = Bus; ReadConfig = DeviceContext->ReadConfig; WriteConfig = DeviceContext->WriteConfig; BusRegister = (ULONG)ReadConfig(Bus, DeviceNumber, Function, PCI_BRIDGE_BUS_NUMBERS_OFFSET, sizeof(ULONG)); // // Save the secondary and subordinate bus numbers that were programmed by // the firmware. The final secondary bus number will be retrieved from the // allocated resources; they should match. The subordinate bus number is // the highest bus number underneath this bridge and all bus numbers // beneath a given bridge must be contiguous. A depth-first search would // need to be performed before the system enumerates the bridges in order // to correctly calculate the subordinate bus numbers. For now, rely on the // firmware to have done the work. // OriginalSecondaryBusNumber = (BusRegister & PCI_BRIDGE_SECONDARY_BUS_MASK) >> PCI_BRIDGE_SECONDARY_BUS_SHIFT; SubordinateBusNumber = (BusRegister & PCI_BRIDGE_SUBORDINATE_BUS_MASK) >> PCI_BRIDGE_SUBORDINATE_BUS_SHIFT; // // Read the control register. // ControlRegister = (USHORT)ReadConfig(Bus, DeviceNumber, Function, PCI_CONTROL_OFFSET, sizeof(USHORT)); ControlRegister |= PCI_CONTROL_BUS_MASTER_ENABLED | PCI_CONTROL_SPECIAL_CYCLES_ENABLED | PCI_CONTROL_WRITE_INVALIDATE_ENABLED | PCI_CONTROL_SERR_ENABLED; // // Disable all decoding in preparation for setting the BARs. // WriteConfig(Bus, DeviceNumber, Function, PCI_CONTROL_OFFSET, sizeof(USHORT), 0); // // Loop over all the given resources, and extract the necessary items. // Don't program anything in until everything's retrieved. // Allocation = IoGetNextResourceAllocation(AllocationList, NULL); while (Allocation != NULL) { // // Skip zero length allocations. // if (Allocation->Length == 0) { Allocation = IoGetNextResourceAllocation(AllocationList, Allocation); continue; } // // Save the bus number. // if (Allocation->Type == ResourceTypeBusNumber) { ASSERT((UCHAR)Allocation->Allocation == Allocation->Allocation); ASSERT(Allocation->Length == 1); BusRegister &= PCI_BRIDGE_SECONDARY_LATENCY_TIMER_MASK; SecondaryBusNumber = (UCHAR)Allocation->Allocation; // // Save the I/O port window. // } else if (Allocation->Type == ResourceTypeIoPort) { ControlRegister |= PCI_CONTROL_IO_DECODE_ENABLED; Limit = Allocation->Allocation + Allocation->Length - PCI_BRIDGE_IO_GRANULARITY; IoPortRegister = (USHORT)(((Allocation->Allocation >> PCI_BRIDGE_IO_BASE_ADDRESS_SHIFT) & PCI_BRIDGE_IO_BASE_MASK) | (Limit & PCI_BRIDGE_IO_LIMIT_MASK)); IoPortHigh = (ULONG)((Allocation->Allocation >> PCI_BRIDGE_IO_BASE_HIGH_ADDRESS_SHIFT) & PCI_BRIDGE_IO_BASE_HIGH_MASK); if (IoPortHigh != 0) { IoPortRegister |= PCI_BRIDGE_IO_BASE_DECODE_32_BIT; } if ((ULONG)(Limit & PCI_BRIDGE_IO_LIMIT_HIGH_MASK) != 0) { IoPortRegister |= PCI_BRIDGE_IO_LIMIT_DECODE_32_BIT; } IoPortHigh |= (ULONG)(Limit & PCI_BRIDGE_IO_LIMIT_HIGH_MASK); // // Save the non-prefetchable (MMIO) memory window. // } else if ((Allocation->Type == ResourceTypePhysicalAddressSpace) && ((Allocation->Characteristics & MEMORY_CHARACTERISTIC_PREFETCHABLE) == 0)) { ControlRegister |= PCI_CONTROL_MEMORY_DECODE_ENABLED; Limit = Allocation->Allocation + Allocation->Length - PCI_BRIDGE_MEMORY_GRANULARITY; MemoryRegister = (ULONG)(((Allocation->Allocation >> PCI_BRIDGE_MEMORY_BASE_ADDRESS_SHIFT) & PCI_BRIDGE_MEMORY_BASE_MASK) | (Limit & PCI_BRIDGE_MEMORY_LIMIT_MASK)); // // Save the prefetchable memory window. // } else if ((Allocation->Type == ResourceTypePhysicalAddressSpace) && ((Allocation->Characteristics & MEMORY_CHARACTERISTIC_PREFETCHABLE) != 0)) { ControlRegister |= PCI_CONTROL_MEMORY_DECODE_ENABLED; Limit = Allocation->Allocation + Allocation->Length - PCI_BRIDGE_MEMORY_GRANULARITY; PrefetchableMemoryLow = (ULONG)(((Allocation->Allocation >> PCI_BRIDGE_PREFETCHABLE_MEMORY_BASE_ADDRESS_SHIFT) & PCI_BRIDGE_PREFETCHABLE_MEMORY_BASE_MASK) | (Limit & PCI_BRIDGE_PREFETCHABLE_MEMORY_LIMIT_MASK)); PrefetchableMemoryBaseHigh = (ULONG)(Allocation->Allocation >> PCI_BRIDGE_PREFETCHABLE_MEMORY_HIGH_ADDRESS_SHIFT); if (PrefetchableMemoryBaseHigh != 0) { PrefetchableMemoryLow |= PCI_BRIDGE_PREFETCHABLE_MEMORY_BASE_DECODE_64_BIT; } PrefetchableMemoryLimitHigh = (ULONG)(Limit >> PCI_BRIDGE_PREFETCHABLE_MEMORY_HIGH_ADDRESS_SHIFT); if (PrefetchableMemoryLimitHigh != 0) { PrefetchableMemoryLow |= PCI_BRIDGE_PREFETCHABLE_MEMORY_LIMIT_DECODE_64_BIT; } } // // Loop on to the next allocation. // Allocation = IoGetNextResourceAllocation(AllocationList, Allocation); } // // The secondary bus number that was allocated for this bridge should be // equal to the number allocated by the firmware at boot. This dependency // is taken to avoid doing a depth-first search to determine the correct // subordinate bus number for each bridge. // ASSERT(SecondaryBusNumber == OriginalSecondaryBusNumber); // // Set up the bus number register value now that the information has been // extracted. // BusRegister |= PrimaryBusNumber | (SecondaryBusNumber << PCI_BRIDGE_SECONDARY_BUS_SHIFT) | (SubordinateBusNumber << PCI_BRIDGE_SUBORDINATE_BUS_SHIFT); // // Okay, everything's accounted for. Write the values into the bridge. // WriteConfig(Bus, DeviceNumber, Function, PCI_BRIDGE_BUS_NUMBERS_OFFSET, sizeof(ULONG), BusRegister); WriteConfig(Bus, DeviceNumber, Function, PCI_BRIDGE_IO_BAR_OFFSET, sizeof(USHORT), IoPortRegister); WriteConfig(Bus, DeviceNumber, Function, PCI_BRIDGE_IO_HIGH_BAR_OFFSET, sizeof(ULONG), IoPortHigh); WriteConfig(Bus, DeviceNumber, Function, PCI_BRIDGE_MEMORY_BAR_OFFSET, sizeof(ULONG), MemoryRegister); WriteConfig(Bus, DeviceNumber, Function, PCI_BRIDGE_PREFETCHABLE_MEMORY_BAR_OFFSET, sizeof(ULONG), PrefetchableMemoryLow); WriteConfig(Bus, DeviceNumber, Function, PCI_BRIDGE_PREFETCHABLE_MEMORY_BASE_HIGH_OFFSET, sizeof(ULONG), PrefetchableMemoryBaseHigh); WriteConfig(Bus, DeviceNumber, Function, PCI_BRIDGE_PREFETCHABLE_MEMORY_LIMIT_HIGH_OFFSET, sizeof(ULONG), PrefetchableMemoryLimitHigh); // // Write out the control register to enable address decoding. // WriteConfig(Bus, DeviceNumber, Function, PCI_CONTROL_OFFSET, sizeof(USHORT), ControlRegister); Status = STATUS_SUCCESS; SetBridgeDeviceResourcesEnd: return Status; } ULONG PcipFindDevice ( PPCI_DEVICE ParentBus, UCHAR Device, UCHAR Function ) /*++ Routine Description: This routine searches for a PCI device matching the given device and function in the child list of another device. Arguments: ParentBus - Supplies a pointer to the PCI device whose children should be searched. Device - Supplies the PCI device slot number to search for. Function - Supplies the function number to search for. Return Value: Returns the index of the child in the device's child array, or MAX_ULONG if the device could not be found. --*/ { ULONG ChildIndex; for (ChildIndex = 0; ChildIndex < ParentBus->ChildCount; ChildIndex += 1) { if ((ParentBus->ChildrenData[ChildIndex]->DeviceNumber == Device) && (ParentBus->ChildrenData[ChildIndex]->Function == Function)) { return ChildIndex; } } return MAX_ULONG; } ULONG PcipGetNewChildIndex ( PPCI_DEVICE ParentBus ) /*++ Routine Description: This routine allocates space in the list of child devices, and also allocates space for the child information. Arguments: ParentBus - Supplies a pointer to the parent bus device where a new device is about to be added. Return Value: Returns an index into the child array where the new child device should be placed, and where the child device information buffer is stored. MAX_ULONG on error. --*/ { ULONG AllocationCount; ULONG AllocationSize; PDEVICE *NewChildren; PPCI_CHILD *NewChildrenData; ULONG NewIndex; NewChildren = NULL; NewChildrenData = NULL; NewIndex = MAX_ULONG; ASSERT(ParentBus->ChildCount < MAX_PCI_DEVICES); if (ParentBus->ChildCount >= MAX_PCI_DEVICES) { goto GetNewChildIndexEnd; } // // If there's room in the array, simply use that. // if (ParentBus->ChildCount < ParentBus->ChildSize) { NewIndex = ParentBus->ChildCount; // // There's no room in the array. Allocate a new array, copy the old // contents in, and free the old array. // } else { AllocationCount = ParentBus->ChildSize * 2; if (AllocationCount < PCI_INITIAL_CHILD_COUNT) { AllocationCount = PCI_INITIAL_CHILD_COUNT; } if (AllocationCount > MAX_PCI_DEVICES) { AllocationCount = MAX_PCI_DEVICES; } // // Allocate the new array. // AllocationSize = (sizeof(PDEVICE) + sizeof(PPCI_CHILD)) * AllocationCount; NewChildren = MmAllocatePagedPool(AllocationSize, PCI_ALLOCATION_TAG); if (NewChildren == NULL) { goto GetNewChildIndexEnd; } NewChildrenData = (PPCI_CHILD *)(NewChildren + AllocationCount); if (ParentBus->Children != NULL) { // // Copy the old contents over. // RtlCopyMemory(NewChildren, ParentBus->Children, sizeof(PDEVICE) * ParentBus->ChildCount); RtlCopyMemory(NewChildrenData, ParentBus->ChildrenData, sizeof(PPCI_DEVICE) * ParentBus->ChildCount); // // Free the old contents and update the pointers. // MmFreePagedPool(ParentBus->Children); } ParentBus->Children = NewChildren; ParentBus->ChildrenData = NewChildrenData; ParentBus->ChildSize = AllocationCount; NewIndex = ParentBus->ChildCount; NewChildren = NULL; } // // Allocate a new PCI child structure. // ParentBus->ChildrenData[NewIndex] = MmAllocatePagedPool(sizeof(PCI_CHILD), PCI_ALLOCATION_TAG); if (ParentBus->ChildrenData[NewIndex] == NULL) { NewIndex = MAX_ULONG; goto GetNewChildIndexEnd; } RtlZeroMemory(ParentBus->ChildrenData[NewIndex], sizeof(PCI_CHILD)); GetNewChildIndexEnd: if (NewChildren != NULL) { MmFreePagedPool(NewChildren); } return NewIndex; } KSTATUS PcipQueryInterface ( PIRP Irp, PPCI_DEVICE PciDevice ) /*++ Routine Description: This routine responds to interface requests. Arguments: Irp - Supplies a pointer to the Query Interface IRP. PciDevice - Supplies a pointer to the PCI device context relating to this device. Return Value: Status code. --*/ { PINTERFACE_ACPI_BUS_ADDRESS BusAddressInterface; PINTERFACE_PCI_BUS_DEVICE BusDeviceInterface; BOOL Match; PINTERFACE_PCI_CONFIG_ACCESS PciConfigInterface; PINTERFACE_SPECIFIC_PCI_CONFIG_ACCESS SpecificPciConfigInterface; KSTATUS Status; ASSERT((Irp->MajorCode == IrpMajorStateChange) && (Irp->MinorCode == IrpMinorQueryInterface)); if (Irp->U.QueryInterface.Interface == NULL) { return STATUS_INVALID_PARAMETER; } Status = STATUS_SUCCESS; // // Handle PCI config access interface requests. // Match = RtlAreUuidsEqual(&PciConfigSpaceUuid, Irp->U.QueryInterface.Interface); if (Match != FALSE) { if (Irp->U.QueryInterface.InterfaceBuffer != NULL) { // // Copy the interface into the buffer, assuming its big enough. // if (Irp->U.QueryInterface.InterfaceBufferSize != sizeof(INTERFACE_PCI_CONFIG_ACCESS)) { Status = STATUS_INCORRECT_BUFFER_SIZE; Irp->U.QueryInterface.InterfaceBufferSize = sizeof(INTERFACE_PCI_CONFIG_ACCESS); goto QueryInterfaceEnd; } PciConfigInterface = Irp->U.QueryInterface.InterfaceBuffer; PciConfigInterface->ReadPciConfig = PcipInterfaceReadConfigSpace; PciConfigInterface->WritePciConfig = PcipInterfaceWriteConfigSpace; PciConfigInterface->DeviceToken = PciDevice; // // The buffer is NULL, indicating the caller just wanted to know if the // interface was out there. Fill out the size and return success. // } else { Irp->U.QueryInterface.InterfaceBufferSize = sizeof(INTERFACE_PCI_CONFIG_ACCESS); } goto QueryInterfaceEnd; } // // Handle specific PCI config access interface requests. // Match = RtlAreUuidsEqual(&PciSpecificConfigSpaceUuid, Irp->U.QueryInterface.Interface); if (Match != FALSE) { ASSERT((PciDevice->Type == PciDeviceBus) || (PciDevice->Type == PciDeviceBridge)); if (Irp->U.QueryInterface.InterfaceBuffer != NULL) { // // Copy the interface into the buffer, assuming its big enough. // if (Irp->U.QueryInterface.InterfaceBufferSize != sizeof(INTERFACE_SPECIFIC_PCI_CONFIG_ACCESS)) { Status = STATUS_INCORRECT_BUFFER_SIZE; Irp->U.QueryInterface.InterfaceBufferSize = sizeof(INTERFACE_SPECIFIC_PCI_CONFIG_ACCESS); goto QueryInterfaceEnd; } SpecificPciConfigInterface = Irp->U.QueryInterface.InterfaceBuffer; SpecificPciConfigInterface->ReadPciConfig = PcipInterfaceReadSpecificConfigSpace; SpecificPciConfigInterface->WritePciConfig = PcipInterfaceWriteSpecificConfigSpace; SpecificPciConfigInterface->DeviceToken = PciDevice; // // The buffer is NULL, indicating the caller just wanted to know if the // interface was out there. Fill out the size and return success. // } else { Irp->U.QueryInterface.InterfaceBufferSize = sizeof(INTERFACE_SPECIFIC_PCI_CONFIG_ACCESS); } goto QueryInterfaceEnd; } // // Handle ACPI bus address interface requests. // if (PciDevice->Type == PciDeviceFunction) { Match = RtlAreUuidsEqual(&PciAcpiBusAddressUuid, Irp->U.QueryInterface.Interface); if (Match != FALSE) { ASSERT(PciDevice->Type == PciDeviceFunction); if (Irp->U.QueryInterface.InterfaceBuffer != NULL) { // // Copy the interface into the buffer, assuming its big enough. // if (Irp->U.QueryInterface.InterfaceBufferSize != sizeof(INTERFACE_ACPI_BUS_ADDRESS)) { Status = STATUS_INCORRECT_BUFFER_SIZE; Irp->U.QueryInterface.InterfaceBufferSize = sizeof(INTERFACE_ACPI_BUS_ADDRESS); goto QueryInterfaceEnd; } BusAddressInterface = Irp->U.QueryInterface.InterfaceBuffer; BusAddressInterface->BusAddress = (PciDevice->DeviceNumber << 16) | PciDevice->FunctionNumber; // // The buffer is NULL, indicating the caller just wanted to know if // the interface was out there. Fill out the size and return // success. // } else { Irp->U.QueryInterface.InterfaceBufferSize = sizeof(INTERFACE_ACPI_BUS_ADDRESS); } goto QueryInterfaceEnd; } } // // Handle internal PCI bus driver context requests. The function driver for // bridges should not respond to this, leave it for the root bus function // driver or a PCI bus driver. // if ((PciDevice->Type == PciDeviceBus) || (PciDevice->Type == PciDeviceFunction)) { Match = RtlAreUuidsEqual(&PciBusDriverDeviceUuid, Irp->U.QueryInterface.Interface); if (Match != FALSE) { if (Irp->U.QueryInterface.InterfaceBuffer != NULL) { // // Copy the interface into the buffer, assuming its big enough. // if (Irp->U.QueryInterface.InterfaceBufferSize != sizeof(INTERFACE_PCI_BUS_DEVICE)) { ASSERT(FALSE); Status = STATUS_INCORRECT_BUFFER_SIZE; Irp->U.QueryInterface.InterfaceBufferSize = sizeof(INTERFACE_PCI_BUS_DEVICE); goto QueryInterfaceEnd; } BusDeviceInterface = Irp->U.QueryInterface.InterfaceBuffer; BusDeviceInterface->BusDevice = PciDevice; // // The buffer is NULL, indicating the caller just wanted to know if // the interface was out there. Fill out the size and return // success. // } else { Irp->U.QueryInterface.InterfaceBufferSize = sizeof(INTERFACE_PCI_BUS_DEVICE); } goto QueryInterfaceEnd; } } // // The interface is not exposed by this PCI device. // Status = STATUS_NO_INTERFACE; QueryInterfaceEnd: return Status; } KSTATUS PcipInterfaceReadConfigSpace ( PVOID DeviceToken, ULONG Offset, ULONG AccessSize, PULONGLONG Value ) /*++ Routine Description: This routine reads from a device's PCI configuration space. Arguments: DeviceToken - Supplies the device token supplied when the interface was acquired. Offset - Supplies the offset in bytes into the PCI configuration space to read. AccessSize - Supplies the size of the access to make. Valid values are 1, 2, 4, and 8. Value - Supplies a pointer where the value read from PCI configuration space will be returned on success. Return Value: Status code. --*/ { ULONGLONG DataRead; PPCI_DEVICE PciDevice; if (Offset > 0xFF) { return STATUS_NOT_SUPPORTED; } PciDevice = DeviceToken; DataRead = PciDevice->ReadConfig(PciDevice->BusNumber, PciDevice->DeviceNumber, PciDevice->FunctionNumber, Offset, AccessSize); *Value = DataRead; return STATUS_SUCCESS; } KSTATUS PcipInterfaceWriteConfigSpace ( PVOID DeviceToken, ULONG Offset, ULONG AccessSize, ULONGLONG Value ) /*++ Routine Description: This routine writes to a device's PCI configuration space. Arguments: DeviceToken - Supplies the device token supplied when the interface was acquired. AccessSize - Supplies the size of the access to make. Valid values are 1, 2, 4, and 8. Offset - Supplies the offset in bytes into the PCI configuration space to write. Value - Supplies the value to write into PCI configuration space. Return Value: Status code. --*/ { PPCI_DEVICE PciDevice; if (Offset > 0xFF) { return STATUS_NOT_SUPPORTED; } PciDevice = DeviceToken; PciDevice->WriteConfig(PciDevice->BusNumber, PciDevice->DeviceNumber, PciDevice->FunctionNumber, Offset, AccessSize, Value); return STATUS_SUCCESS; } KSTATUS PcipInterfaceReadSpecificConfigSpace ( PVOID DeviceToken, ULONG BusNumber, ULONG DeviceNumber, ULONG FunctionNumber, ULONG Offset, ULONG AccessSize, PULONGLONG Value ) /*++ Routine Description: This routine reads from a specific device's PCI configuration space. Arguments: DeviceToken - Supplies the device token supplied when the interface was acquired. BusNumber - Supplies the bus number of the device whose PCI configuration space should be read from. DeviceNumber - Supplies the device number of the device whose PCI configuration space should be read from. FunctionNumber - Supplies the function number of the device whose PCI configuration space should be read from. Offset - Supplies the offset in bytes into the PCI configuration space to read. AccessSize - Supplies the size of the access to make. Valid values are 1, 2, 4, and 8. Value - Supplies a pointer where the value read from PCI configuration space will be returned on success. Return Value: Status code. --*/ { ULONGLONG DataRead; PPCI_DEVICE PciDevice; if (Offset > 0xFF) { return STATUS_NOT_SUPPORTED; } PciDevice = DeviceToken; ASSERT((PciDevice->Type == PciDeviceBus) || (PciDevice->Type == PciDeviceBridge)); DataRead = PciDevice->ReadConfig(BusNumber, DeviceNumber, FunctionNumber, Offset, AccessSize); *Value = DataRead; return STATUS_SUCCESS; } KSTATUS PcipInterfaceWriteSpecificConfigSpace ( PVOID DeviceToken, ULONG BusNumber, ULONG DeviceNumber, ULONG FunctionNumber, ULONG Offset, ULONG AccessSize, ULONGLONG Value ) /*++ Routine Description: This routine writes to a specific device's PCI configuration space. Arguments: DeviceToken - Supplies the device token supplied when the interface was acquired. BusNumber - Supplies the bus number of the device whose PCI configuration space should be written to. DeviceNumber - Supplies the device number of the device whose PCI configuration space should be written to. FunctionNumber - Supplies the function number of the device whose PCI configuration space should be written to. Offset - Supplies the offset in bytes into the PCI configuration space to write. AccessSize - Supplies the size of the access to make. Valid values are 1, 2, 4, and 8. Value - Supplies the value to write into PCI configuration space. Return Value: Status code. --*/ { PPCI_DEVICE PciDevice; if (Offset > 0xFF) { return STATUS_NOT_SUPPORTED; } PciDevice = DeviceToken; ASSERT((PciDevice->Type == PciDeviceBus) || (PciDevice->Type == PciDeviceBridge)); PciDevice->WriteConfig(BusNumber, DeviceNumber, FunctionNumber, Offset, AccessSize, Value); return STATUS_SUCCESS; } KSTATUS PcipStartBusDevice ( PIRP StartIrp, PPCI_DEVICE DeviceContext ) /*++ Routine Description: This routine starts a PCI bus. Arguments: StartIrp - Supplies a pointer to the start IRP. DeviceContext - Supplies a pointer to the PCI bus or bridge. Return Value: Status code. --*/ { PRESOURCE_ALLOCATION Allocation; PRESOURCE_ALLOCATION_LIST AllocationList; BOOL BusNumberArbiterCreated; BOOL IoPortArbiterCreated; BOOL MemoryArbiterCreated; PPCI_DEVICE Parent; KSTATUS Status; ASSERT(StartIrp->MinorCode == IrpMinorStartDevice); ASSERT((DeviceContext->Type == PciDeviceBus) || (DeviceContext->Type == PciDeviceBridge)); // // Bridges need to query the interface of the bus driver to get // configuration space access. // if (DeviceContext->ReadConfig == NULL) { ASSERT(DeviceContext->Type == PciDeviceBridge); Status = PcipGetBusDriverDevice(StartIrp->Device, &Parent); if (!KSUCCESS(Status)) { goto StartBusDeviceEnd; } DeviceContext->ReadConfig = Parent->ReadConfig; DeviceContext->WriteConfig = Parent->WriteConfig; } ASSERT((DeviceContext->ReadConfig != NULL) && (DeviceContext->WriteConfig != NULL)); // // Create the "specific PCI Config Space" access interface. // Status = PcipCreateBusInterfaces(StartIrp->Device, DeviceContext); if (!KSUCCESS(Status)) { goto StartBusDeviceEnd; } BusNumberArbiterCreated = FALSE; IoPortArbiterCreated = FALSE; MemoryArbiterCreated = FALSE; Status = STATUS_SUCCESS; // // Loop through every resource given to the bus/bridge, and expose an // arbiter for child devices. // AllocationList = StartIrp->U.StartDevice.ProcessorLocalResources; if (AllocationList == NULL) { goto StartBusDeviceEnd; } Allocation = IoGetNextResourceAllocation(AllocationList, NULL); while (Allocation != NULL) { // // Only create arbiters for expected types. // switch (Allocation->Type) { case ResourceTypeBusNumber: // // Create a bus number arbiter if one hasn't been created yet and // more than one bus number was doled out. Keep the first bus // number for this bus itself. // if (Allocation->Length > 1) { ASSERT(Allocation->Allocation == DeviceContext->BusNumber); if (BusNumberArbiterCreated == FALSE) { Status = IoCreateResourceArbiter(StartIrp->Device, Allocation->Type); if (!KSUCCESS(Status)) { goto StartBusDeviceEnd; } BusNumberArbiterCreated = TRUE; } Status = IoAddFreeSpaceToArbiter(StartIrp->Device, Allocation->Type, Allocation->Allocation + 1, Allocation->Length - 1, Allocation->Characteristics, Allocation, 0); // // If only one bus number was handed out, this must be a bridge. // Save that bus number for downstream config accesses later. // } else { ASSERT(Allocation->Length == 1); ASSERT((UCHAR)Allocation->Allocation == Allocation->Allocation); DeviceContext->BusNumber = (UCHAR)(Allocation->Allocation); } break; case ResourceTypePhysicalAddressSpace: // // Create an address space arbiter if one hasn't been created yet. // if (MemoryArbiterCreated == FALSE) { Status = IoCreateResourceArbiter(StartIrp->Device, Allocation->Type); if (!KSUCCESS(Status)) { goto StartBusDeviceEnd; } MemoryArbiterCreated = TRUE; } Status = IoAddFreeSpaceToArbiter(StartIrp->Device, Allocation->Type, Allocation->Allocation, Allocation->Length, Allocation->Characteristics, Allocation, 0); break; case ResourceTypeIoPort: // // Create an I/O port arbiter if one hasn't been created yet. // if (IoPortArbiterCreated == FALSE) { Status = IoCreateResourceArbiter(StartIrp->Device, Allocation->Type); if (!KSUCCESS(Status)) { goto StartBusDeviceEnd; } IoPortArbiterCreated = TRUE; } Status = IoAddFreeSpaceToArbiter(StartIrp->Device, Allocation->Type, Allocation->Allocation, Allocation->Length, Allocation->Characteristics, Allocation, 0); break; default: break; } if (!KSUCCESS(Status)) { goto StartBusDeviceEnd; } Allocation = IoGetNextResourceAllocation(AllocationList, Allocation); } StartBusDeviceEnd: return Status; } KSTATUS PcipCreateFunctionInterfaces ( PDEVICE Device, PPCI_DEVICE PciDevice ) /*++ Routine Description: This routine creates the exposed interfaces for a PCI device. Arguments: Device - Supplies a pointer to the device to create interfaces for. PciDevice - Supplies a pointer to the PCI device context. Return Value: Status code. --*/ { PINTERFACE_ACPI_BUS_ADDRESS BusAddressInterface; PINTERFACE_PCI_CONFIG_ACCESS PciConfigInterface; KSTATUS Status; BusAddressInterface = NULL; // // Create the PCI config access interface. // PciConfigInterface = MmAllocateNonPagedPool( sizeof(INTERFACE_PCI_CONFIG_ACCESS), PCI_ALLOCATION_TAG); if (PciConfigInterface == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto CreateFunctionInterfacesEnd; } RtlZeroMemory(PciConfigInterface, sizeof(INTERFACE_PCI_CONFIG_ACCESS)); PciConfigInterface->ReadPciConfig = PcipInterfaceReadConfigSpace; PciConfigInterface->WritePciConfig = PcipInterfaceWriteConfigSpace; PciConfigInterface->DeviceToken = PciDevice; PciDevice->PciConfigInterface = PciConfigInterface; // // Create the ACPI bus address interface. // BusAddressInterface = MmAllocateNonPagedPool( sizeof(INTERFACE_ACPI_BUS_ADDRESS), PCI_ALLOCATION_TAG); if (BusAddressInterface == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto CreateFunctionInterfacesEnd; } RtlZeroMemory(BusAddressInterface, sizeof(INTERFACE_ACPI_BUS_ADDRESS)); BusAddressInterface->BusAddress = (PciDevice->DeviceNumber << 16) | PciDevice->FunctionNumber; PciDevice->AcpiBusAddressInterface = BusAddressInterface; // // Enumerate the devices to the system. // Status = IoCreateInterface(&PciConfigSpaceUuid, Device, PciConfigInterface, sizeof(INTERFACE_PCI_CONFIG_ACCESS)); if (!KSUCCESS(Status)) { // // Allow this to fail with a duplicate entry if the device is a bridge, // as the bridge's functional driver will have already created this // interface. // if ((Status != STATUS_DUPLICATE_ENTRY) || (PciDevice->DeviceIsBridge == FALSE)) { goto CreateFunctionInterfacesEnd; } } Status = IoCreateInterface(&PciAcpiBusAddressUuid, Device, BusAddressInterface, sizeof(INTERFACE_ACPI_BUS_ADDRESS)); if (!KSUCCESS(Status)) { IoDestroyInterface(&PciConfigSpaceUuid, Device, PciConfigInterface); goto CreateFunctionInterfacesEnd; } // // Attempt to create the MSI/MSI-X context and interface for this function // device. // Status = PcipMsiCreateContextAndInterface(Device, PciDevice); if (!KSUCCESS(Status)) { IoDestroyInterface(&PciConfigSpaceUuid, Device, PciConfigInterface); IoDestroyInterface(&PciAcpiBusAddressUuid, Device, BusAddressInterface); goto CreateFunctionInterfacesEnd; } Status = STATUS_SUCCESS; CreateFunctionInterfacesEnd: if (!KSUCCESS(Status)) { if (PciConfigInterface != NULL) { MmFreeNonPagedPool(PciConfigInterface); } if (BusAddressInterface != NULL) { MmFreeNonPagedPool(BusAddressInterface); } PciDevice->PciConfigInterface = NULL; PciDevice->AcpiBusAddressInterface = NULL; if (PciDevice->MsiContext != NULL) { PcipMsiDestroyContextAndInterface(Device, PciDevice); } } return Status; } KSTATUS PcipCreateBusInterfaces ( PDEVICE Device, PPCI_DEVICE PciDevice ) /*++ Routine Description: This routine creates the exposed interfaces for a PCI device. Arguments: Device - Supplies a pointer to the device to create interfaces for. PciDevice - Supplies a pointer to the PCI device context. Return Value: Status code. --*/ { PINTERFACE_SPECIFIC_PCI_CONFIG_ACCESS SpecificPciConfigInterface; KSTATUS Status; // // Create the specific PCI config access interface. // SpecificPciConfigInterface = MmAllocateNonPagedPool( sizeof(INTERFACE_SPECIFIC_PCI_CONFIG_ACCESS), PCI_ALLOCATION_TAG); if (SpecificPciConfigInterface == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto CreateInterfacesEnd; } RtlZeroMemory(SpecificPciConfigInterface, sizeof(INTERFACE_SPECIFIC_PCI_CONFIG_ACCESS)); SpecificPciConfigInterface->ReadPciConfig = PcipInterfaceReadSpecificConfigSpace; SpecificPciConfigInterface->WritePciConfig = PcipInterfaceWriteSpecificConfigSpace; SpecificPciConfigInterface->DeviceToken = PciDevice; // // Expose the interface to the system. // Status = IoCreateInterface(&PciSpecificConfigSpaceUuid, Device, SpecificPciConfigInterface, sizeof(INTERFACE_SPECIFIC_PCI_CONFIG_ACCESS)); if (!KSUCCESS(Status)) { goto CreateInterfacesEnd; } PciDevice->SpecificPciConfigInterface = SpecificPciConfigInterface; Status = STATUS_SUCCESS; CreateInterfacesEnd: if (!KSUCCESS(Status)) { if (SpecificPciConfigInterface != NULL) { MmFreeNonPagedPool(SpecificPciConfigInterface); } PciDevice->SpecificPciConfigInterface = NULL; } return Status; } PSTR PcipGetClassId ( ULONG ClassCode ) /*++ Routine Description: This routine returns the class string for the given PCI class code. Arguments: ClassCode - Supplies the class code. Return Value: Returns a pointer to a string describing the class. NULL if no class could be determined. --*/ { UCHAR Class; USHORT Subclass; Class = PCI_CLASS_CODE(ClassCode); Subclass = PCI_SUBCLASS_AND_INTERFACE(ClassCode); switch (Class) { // // Unimplemented or unknown class codes. // case PCI_CLASS_UNKNOWN: if (Subclass == PCI_CLASS_UNKNOWN_VGA) { return "VGA"; } break; case PCI_CLASS_MASS_STORAGE: if ((Subclass & PCI_CLASS_MASS_STORAGE_IDE_MASK) == PCI_CLASS_MASS_STORAGE_IDE) { return "IDE"; } break; case PCI_CLASS_BRIDGE: switch (Subclass) { case PCI_CLASS_BRIDGE_ISA: return "ISA"; case PCI_CLASS_BRIDGE_PCI: return PCI_BRIDGE_CLASS_ID; case PCI_CLASS_BRIDGE_PCI_SUBTRACTIVE: return PCI_SUBTRACTIVE_BRIDGE_CLASS_ID; default: break; } break; case PCI_CLASS_SERIAL_BUS: switch (Subclass) { case PCI_CLASS_SERIAL_BUS_USB_UHCI: return "UHCI"; case PCI_CLASS_SERIAL_BUS_USB_OHCI: return "OHCI"; case PCI_CLASS_SERIAL_BUS_USB_EHCI: return "EHCI"; default: break; } break; case PCI_CLASS_NETWORK: case PCI_CLASS_DISPLAY: case PCI_CLASS_MULTIMEDIA: case PCI_CLASS_MEMORY: break; case PCI_CLASS_SIMPLE_COMMUNICATION: switch (Subclass) { case PCI_CLASS_SIMPLE_COMMUNICATION_XT_UART: case PCI_CLASS_SIMPLE_COMMUNICATION_16450: case PCI_CLASS_SIMPLE_COMMUNICATION_16550: return "Serial16550"; } break; case PCI_CLASS_GENERAL_PERIPHERAL: switch (Subclass) { case PCI_CLASS_GENERAL_SD_HOST_NO_DMA: return "SdHostPio"; case PCI_CLASS_GENERAL_SD_HOST: return "SdHost"; default: break; } break; case PCI_CLASS_INPUT: case PCI_CLASS_DOCKING_STATION: case PCI_CLASS_PROCESSOR: case PCI_CLASS_WIRELESS: case PCI_CLASS_INTELLIGENT_IO: case PCI_CLASS_SATELLITE_COMMUNICATION: case PCI_CLASS_ENCRYPTION: case PCI_CLASS_DATA_ACQUISITION: case PCI_CLASS_VENDOR: break; } return NULL; } KSTATUS PcipGetBusDriverDevice ( PDEVICE OsDevice, PPCI_DEVICE *BusDriverDevice ) /*++ Routine Description: This routine returns the bus driver's PCI device structure. Arguments: OsDevice - Supplies a pointer to the OS device. BusDriverDevice - Supplies a pointer where a pointer to the bus driver's device will be returned on success. Return Value: Status code. --*/ { INTERFACE_PCI_BUS_DEVICE Interface; PIRP QueryInterfaceIrp; KSTATUS Status; *BusDriverDevice = NULL; // // Allocate and send an IRP to the bus driver requesting access // to the PCI config interface. // QueryInterfaceIrp = IoCreateIrp(OsDevice, IrpMajorStateChange, 0); if (QueryInterfaceIrp == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto GetBusDriverDeviceEnd; } QueryInterfaceIrp->MinorCode = IrpMinorQueryInterface; QueryInterfaceIrp->U.QueryInterface.Interface = &PciBusDriverDeviceUuid; QueryInterfaceIrp->U.QueryInterface.InterfaceBuffer = &Interface; QueryInterfaceIrp->U.QueryInterface.InterfaceBufferSize = sizeof(INTERFACE_PCI_BUS_DEVICE); Status = IoSendSynchronousIrp(QueryInterfaceIrp); if (!KSUCCESS(Status)) { goto GetBusDriverDeviceEnd; } Status = IoGetIrpStatus(QueryInterfaceIrp); if (!KSUCCESS(Status)) { goto GetBusDriverDeviceEnd; } *BusDriverDevice = Interface.BusDevice; GetBusDriverDeviceEnd: if (QueryInterfaceIrp != NULL) { IoDestroyIrp(QueryInterfaceIrp); } return Status; }