/* GLOBALS ********************************************************************/
+ULONG PciDebugPortsCount;
+
+RTL_RANGE_LIST PciIsaBitExclusionList;
+RTL_RANGE_LIST PciVgaAndIsaBitExclusionList;
+
/* FUNCTIONS ******************************************************************/
+BOOLEAN
+NTAPI
+PciUnicodeStringStrStr(IN PUNICODE_STRING InputString,
+ IN PCUNICODE_STRING EqualString,
+ IN BOOLEAN CaseInSensitive)
+{
+ UNICODE_STRING PartialString;
+ LONG EqualChars, TotalChars;
+
+ /* Build a partial string with the smaller substring */
+ PartialString.Length = EqualString->Length;
+ PartialString.MaximumLength = InputString->MaximumLength;;
+ PartialString.Buffer = InputString->Buffer;
+
+ /* Check how many characters that need comparing */
+ EqualChars = 0;
+ TotalChars = (InputString->Length - EqualString->Length) / sizeof(WCHAR);
+
+ /* If the substring is bigger, just fail immediately */
+ if (TotalChars < 0) return FALSE;
+
+ /* Keep checking each character */
+ while (!RtlEqualUnicodeString(EqualString, &PartialString, CaseInSensitive))
+ {
+ /* Continue checking until all the required characters are equal */
+ PartialString.Buffer++;
+ PartialString.MaximumLength -= sizeof(WCHAR);
+ if (++EqualChars > TotalChars) return FALSE;
+ }
+
+ /* The string is equal */
+ return TRUE;
+}
+
+BOOLEAN
+NTAPI
+PciStringToUSHORT(IN PWCHAR String,
+ OUT PUSHORT Value)
+{
+ USHORT Short;
+ ULONG Low, High, Length;
+ WCHAR Char;
+
+ /* Initialize everything to zero */
+ Short = 0;
+ Length = 0;
+ while (TRUE)
+ {
+ /* Get the character and set the high byte based on the previous one */
+ Char = *String++;
+ High = 16 * Short;
+
+ /* Check for numbers */
+ if ( Char >= '0' && Char <= '9' )
+ {
+ /* Convert them to a byte */
+ Low = Char - '0';
+ }
+ else if ( Char >= 'A' && Char <= 'F' )
+ {
+ /* Convert upper-case hex letters into a byte */
+ Low = Char - '7';
+ }
+ else if ( Char >= 'a' && Char <= 'f' )
+ {
+ /* Convert lower-case hex letters into a byte */
+ Low = Char - 'W';
+ }
+ else
+ {
+ /* Invalid string, fail the conversion */
+ return FALSE;
+ }
+
+ /* Combine the high and low byte */
+ Short = High | Low;
+
+ /* If 4 letters have been reached, the 16-bit integer should exist */
+ if (++Length >= 4)
+ {
+ /* Return it to the caller */
+ *Value = Short;
+ return TRUE;
+ }
+ }
+}
+
+BOOLEAN
+NTAPI
+PciIsSuiteVersion(IN USHORT SuiteMask)
+{
+ ULONGLONG Mask = 0;
+ RTL_OSVERSIONINFOEXW VersionInfo;
+
+ /* Initialize the version information */
+ RtlZeroMemory(&VersionInfo, sizeof(RTL_OSVERSIONINFOEXW));
+ VersionInfo.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW);
+ VersionInfo.wSuiteMask = SuiteMask;
+
+ /* Set the comparison mask and return if the passed suite mask matches */
+ VER_SET_CONDITION(Mask, VER_SUITENAME, VER_AND);
+ return NT_SUCCESS(RtlVerifyVersionInfo(&VersionInfo, VER_SUITENAME, Mask));
+}
+
+BOOLEAN
+NTAPI
+PciIsDatacenter(VOID)
+{
+ BOOLEAN Result;
+ PVOID Value;
+ ULONG ResultLength;
+ NTSTATUS Status;
+
+ /* Assume this isn't Datacenter */
+ Result = FALSE;
+
+ /* First, try opening the setup key */
+ Status = PciGetRegistryValue(L"",
+ L"\\REGISTRY\\MACHINE\\SYSTEM\\CurrentControlSet\\Services\\setupdd",
+ 0,
+ REG_BINARY,
+ &Value,
+ &ResultLength);
+ if (!NT_SUCCESS(Status))
+ {
+ /* This is not an in-progress Setup boot, so query the suite version */
+ Result = PciIsSuiteVersion(VER_SUITE_DATACENTER);
+ }
+ else
+ {
+ /* This scenario shouldn't happen yet, since SetupDD isn't used */
+ UNIMPLEMENTED;
+ while (TRUE);
+ }
+
+ /* Return if this is Datacenter or not */
+ return Result;
+}
+
+BOOLEAN
+NTAPI
+PciOpenKey(IN PWCHAR KeyName,
+ IN HANDLE RootKey,
+ IN ACCESS_MASK DesiredAccess,
+ OUT PHANDLE KeyHandle,
+ OUT PNTSTATUS KeyStatus)
+{
+ NTSTATUS Status;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ UNICODE_STRING KeyString;
+ PAGED_CODE();
+
+ /* Initialize the object attributes */
+ RtlInitUnicodeString(&KeyString, KeyName);
+ InitializeObjectAttributes(&ObjectAttributes,
+ &KeyString,
+ OBJ_CASE_INSENSITIVE,
+ RootKey,
+ NULL);
+
+ /* Open the key, returning a boolean, and the status, if requested */
+ Status = ZwOpenKey(KeyHandle, DesiredAccess, &ObjectAttributes);
+ if (KeyStatus) *KeyStatus = Status;
+ return NT_SUCCESS(Status);
+}
+
+NTSTATUS
+NTAPI
+PciGetRegistryValue(IN PWCHAR ValueName,
+ IN PWCHAR KeyName,
+ IN HANDLE RootHandle,
+ IN ULONG Type,
+ OUT PVOID *OutputBuffer,
+ OUT PULONG OutputLength)
+{
+ NTSTATUS Status;
+ PKEY_VALUE_PARTIAL_INFORMATION PartialInfo;
+ ULONG NeededLength, ActualLength;
+ UNICODE_STRING ValueString;
+ HANDLE KeyHandle;
+ BOOLEAN Result;
+
+ /* So we know what to free at the end of the body */
+ PartialInfo = NULL;
+ KeyHandle = NULL;
+ do
+ {
+ /* Open the key by name, rooted off the handle passed */
+ Result = PciOpenKey(KeyName,
+ RootHandle,
+ KEY_QUERY_VALUE,
+ &KeyHandle,
+ &Status);
+ if (!Result) break;
+
+ /* Query for the size that's needed for the value that was passed in */
+ RtlInitUnicodeString(&ValueString, ValueName);
+ Status = ZwQueryValueKey(KeyHandle,
+ &ValueString,
+ KeyValuePartialInformation,
+ NULL,
+ 0,
+ &NeededLength);
+ ASSERT(!NT_SUCCESS(Status));
+ if (Status != STATUS_BUFFER_TOO_SMALL) break;
+
+ /* Allocate an appropriate buffer for the size that was returned */
+ ASSERT(NeededLength != 0);
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ PartialInfo = ExAllocatePoolWithTag(PagedPool,
+ NeededLength,
+ PCI_POOL_TAG);
+ if (!PartialInfo) break;
+
+ /* Query the actual value information now that the size is known */
+ Status = ZwQueryValueKey(KeyHandle,
+ &ValueString,
+ KeyValuePartialInformation,
+ PartialInfo,
+ NeededLength,
+ &ActualLength);
+ if (!NT_SUCCESS(Status)) break;
+
+ /* Make sure it's of the type that the caller expects */
+ Status = STATUS_INVALID_PARAMETER;
+ if (PartialInfo->Type != Type) break;
+
+ /* Subtract the registry-specific header, to get the data size */
+ ASSERT(NeededLength == ActualLength);
+ NeededLength -= sizeof(KEY_VALUE_PARTIAL_INFORMATION);
+
+ /* Allocate a buffer to hold the data and return it to the caller */
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ *OutputBuffer = ExAllocatePoolWithTag(PagedPool,
+ NeededLength,
+ PCI_POOL_TAG);
+ if (!*OutputBuffer) break;
+
+ /* Copy the data into the buffer and return its length to the caller */
+ RtlCopyMemory(*OutputBuffer, PartialInfo->Data, NeededLength);
+ if (OutputLength) *OutputLength = NeededLength;
+ Status = STATUS_SUCCESS;
+ } while (0);
+
+ /* Close any opened keys and free temporary allocations */
+ if (KeyHandle) ZwClose(KeyHandle);
+ if (PartialInfo) ExFreePoolWithTag(PartialInfo, 0);
+ return Status;
+}
+
+NTSTATUS
+NTAPI
+PciBuildDefaultExclusionLists(VOID)
+{
+ ULONG Start;
+ NTSTATUS Status;
+ ASSERT(PciIsaBitExclusionList.Count == 0);
+ ASSERT(PciVgaAndIsaBitExclusionList.Count == 0);
+
+ /* Initialize the range lists */
+ RtlInitializeRangeList(&PciIsaBitExclusionList);
+ RtlInitializeRangeList(&PciVgaAndIsaBitExclusionList);
+
+ /* Loop x86 I/O ranges */
+ for (Start = 0x100; Start <= 0xFEFF; Start += 0x400)
+ {
+ /* Add the ISA I/O ranges */
+ Status = RtlAddRange(&PciIsaBitExclusionList,
+ Start,
+ Start + 0x2FF,
+ 0,
+ RTL_RANGE_LIST_ADD_IF_CONFLICT,
+ NULL,
+ NULL);
+ if (!NT_SUCCESS(Status)) break;
+
+ /* Add the ISA I/O ranges */
+ Status = RtlAddRange(&PciVgaAndIsaBitExclusionList,
+ Start,
+ Start + 0x2AF,
+ 0,
+ RTL_RANGE_LIST_ADD_IF_CONFLICT,
+ NULL,
+ NULL);
+ if (!NT_SUCCESS(Status)) break;
+
+ /* Add the VGA I/O range for Monochrome Video */
+ Status = RtlAddRange(&PciVgaAndIsaBitExclusionList,
+ Start + 0x2BC,
+ Start + 0x2BF,
+ 0,
+ RTL_RANGE_LIST_ADD_IF_CONFLICT,
+ NULL,
+ NULL);
+ if (!NT_SUCCESS(Status)) break;
+
+ /* Add the VGA I/O range for certain CGA adapters */
+ Status = RtlAddRange(&PciVgaAndIsaBitExclusionList,
+ Start + 0x2E0,
+ Start + 0x2FF,
+ 0,
+ RTL_RANGE_LIST_ADD_IF_CONFLICT,
+ NULL,
+ NULL);
+ if (!NT_SUCCESS(Status)) break;
+
+ /* Success, ranges added done */
+ };
+
+ RtlFreeRangeList(&PciIsaBitExclusionList);
+ RtlFreeRangeList(&PciVgaAndIsaBitExclusionList);
+ return Status;
+}
+
+PPCI_FDO_EXTENSION
+NTAPI
+PciFindParentPciFdoExtension(IN PDEVICE_OBJECT DeviceObject,
+ IN PKEVENT Lock)
+{
+ PPCI_FDO_EXTENSION DeviceExtension;
+ PPCI_PDO_EXTENSION SearchExtension, FoundExtension;
+
+ /* Assume we'll find nothing */
+ SearchExtension = DeviceObject->DeviceExtension;
+ FoundExtension = NULL;
+
+ /* Check if a lock was specified */
+ if (Lock)
+ {
+ /* Wait for the lock to be released */
+ KeEnterCriticalRegion();
+ KeWaitForSingleObject(Lock, Executive, KernelMode, FALSE, NULL);
+ }
+
+ /* Now search for the extension */
+ DeviceExtension = (PPCI_FDO_EXTENSION)PciFdoExtensionListHead.Next;
+ while (DeviceExtension)
+ {
+ /* Acquire this device's lock */
+ KeEnterCriticalRegion();
+ KeWaitForSingleObject(&DeviceExtension->ChildListLock,
+ Executive,
+ KernelMode,
+ FALSE,
+ NULL);
+
+ /* Scan all children PDO, stop when no more PDOs, or found it */
+ for (FoundExtension = DeviceExtension->ChildPdoList;
+ FoundExtension && (FoundExtension != SearchExtension);
+ FoundExtension = FoundExtension->Next);
+
+ /* If we found it, break out */
+ if (FoundExtension) break;
+
+ /* Release this device's lock */
+ KeSetEvent(&DeviceExtension->ChildListLock, IO_NO_INCREMENT, FALSE);
+ KeLeaveCriticalRegion();
+
+ /* Move to the next device */
+ DeviceExtension = (PPCI_FDO_EXTENSION)DeviceExtension->List.Next;
+ }
+
+ /* Check if we had acquired a lock previously */
+ if (Lock)
+ {
+ /* Release it */
+ KeSetEvent(Lock, IO_NO_INCREMENT, FALSE);
+ KeLeaveCriticalRegion();
+ }
+
+ /* Return which extension was found, if any */
+ return DeviceExtension;
+}
+
+VOID
+NTAPI
+PciInsertEntryAtTail(IN PSINGLE_LIST_ENTRY ListHead,
+ IN PPCI_FDO_EXTENSION DeviceExtension,
+ IN PKEVENT Lock)
+{
+ PSINGLE_LIST_ENTRY NextEntry;
+ PAGED_CODE();
+
+ /* Check if a lock was specified */
+ if (Lock)
+ {
+ /* Wait for the lock to be released */
+ KeEnterCriticalRegion();
+ KeWaitForSingleObject(Lock, Executive, KernelMode, FALSE, NULL);
+ }
+
+ /* Loop the list until we get to the end, then insert this entry there */
+ for (NextEntry = ListHead; NextEntry->Next; NextEntry = NextEntry->Next);
+ NextEntry->Next = &DeviceExtension->List;
+
+ /* Check if we had acquired a lock previously */
+ if (Lock)
+ {
+ /* Release it */
+ KeSetEvent(Lock, IO_NO_INCREMENT, FALSE);
+ KeLeaveCriticalRegion();
+ }
+}
+
+VOID
+NTAPI
+PciInsertEntryAtHead(IN PSINGLE_LIST_ENTRY ListHead,
+ IN PSINGLE_LIST_ENTRY Entry,
+ IN PKEVENT Lock)
+{
+ PAGED_CODE();
+
+ /* Check if a lock was specified */
+ if (Lock)
+ {
+ /* Wait for the lock to be released */
+ KeEnterCriticalRegion();
+ KeWaitForSingleObject(Lock, Executive, KernelMode, FALSE, NULL);
+ }
+
+ /* Make the entry point to the current head and make the head point to it */
+ Entry->Next = ListHead->Next;
+ ListHead->Next = Entry;
+
+ /* Check if we had acquired a lock previously */
+ if (Lock)
+ {
+ /* Release it */
+ KeSetEvent(Lock, IO_NO_INCREMENT, FALSE);
+ KeLeaveCriticalRegion();
+ }
+}
+
+VOID
+NTAPI
+PcipLinkSecondaryExtension(IN PSINGLE_LIST_ENTRY List,
+ IN PVOID Lock,
+ IN PPCI_SECONDARY_EXTENSION SecondaryExtension,
+ IN PCI_SIGNATURE ExtensionType,
+ IN PVOID Destructor)
+{
+ PAGED_CODE();
+
+ /* Setup the extension data, and insert it into the primary's list */
+ SecondaryExtension->ExtensionType = ExtensionType;
+ SecondaryExtension->Destructor = Destructor;
+ PciInsertEntryAtHead(List, &SecondaryExtension->List, Lock);
+}
+
+NTSTATUS
+NTAPI
+PciGetDeviceProperty(IN PDEVICE_OBJECT DeviceObject,
+ IN DEVICE_REGISTRY_PROPERTY DeviceProperty,
+ OUT PVOID *OutputBuffer)
+{
+ NTSTATUS Status;
+ ULONG BufferLength, ResultLength;
+ PVOID Buffer;
+ do
+ {
+ /* Query the requested property size */
+ Status = IoGetDeviceProperty(DeviceObject,
+ DeviceProperty,
+ 0,
+ NULL,
+ &BufferLength);
+ if (Status != STATUS_BUFFER_TOO_SMALL)
+ {
+ /* Call should've failed with buffer too small! */
+ DPRINT1("PCI - Unexpected status from GetDeviceProperty, saw %08X, expected %08X.\n",
+ Status,
+ STATUS_BUFFER_TOO_SMALL);
+ *OutputBuffer = NULL;
+ ASSERTMSG(FALSE, "PCI Successfully did the impossible!");
+ break;
+ }
+
+ /* Allocate the required buffer */
+ Buffer = ExAllocatePoolWithTag(PagedPool, BufferLength, 'BicP');
+ if (!Buffer)
+ {
+ /* No memory, fail the request */
+ DPRINT1("PCI - Failed to allocate DeviceProperty buffer (%d bytes).\n", BufferLength);
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ break;
+ }
+
+ /* Do the actual property query call */
+ Status = IoGetDeviceProperty(DeviceObject,
+ DeviceProperty,
+ BufferLength,
+ Buffer,
+ &ResultLength);
+ if (!NT_SUCCESS(Status)) break;
+
+ /* Return the buffer to the caller */
+ ASSERT(BufferLength == ResultLength);
+ *OutputBuffer = Buffer;
+ return STATUS_SUCCESS;
+ } while (FALSE);
+
+ /* Failure path */
+ return STATUS_UNSUCCESSFUL;
+}
+
+NTSTATUS
+NTAPI
+PciSendIoctl(IN PDEVICE_OBJECT DeviceObject,
+ IN ULONG IoControlCode,
+ IN PVOID InputBuffer,
+ IN ULONG InputBufferLength,
+ IN PVOID OutputBuffer,
+ IN ULONG OutputBufferLength)
+{
+ PIRP Irp;
+ NTSTATUS Status;
+ KEVENT Event;
+ IO_STATUS_BLOCK IoStatusBlock;
+ PDEVICE_OBJECT AttachedDevice;
+ PAGED_CODE();
+
+ /* Initialize the pending IRP event */
+ KeInitializeEvent(&Event, SynchronizationEvent, FALSE);
+
+ /* Get a reference to the root PDO (ACPI) */
+ AttachedDevice = IoGetAttachedDeviceReference(DeviceObject);
+ if (!AttachedDevice) return STATUS_INVALID_PARAMETER;
+
+ /* Build the requested IOCTL IRP */
+ Irp = IoBuildDeviceIoControlRequest(IoControlCode,
+ AttachedDevice,
+ InputBuffer,
+ InputBufferLength,
+ OutputBuffer,
+ OutputBufferLength,
+ 0,
+ &Event,
+ &IoStatusBlock);
+ if (!Irp) return STATUS_INSUFFICIENT_RESOURCES;
+
+ /* Send the IOCTL to the driver */
+ Status = IoCallDriver(AttachedDevice, Irp);
+ if (Status == STATUS_PENDING)
+ {
+ /* Wait for a response */
+ KeWaitForSingleObject(&Event,
+ Executive,
+ KernelMode,
+ FALSE,
+ NULL);
+ Status = Irp->IoStatus.Status;
+ }
+
+ /* Take away the reference we took and return the result to the caller */
+ ObDereferenceObject(AttachedDevice);
+ return Status;
+}
+
+PPCI_SECONDARY_EXTENSION
+NTAPI
+PciFindNextSecondaryExtension(IN PSINGLE_LIST_ENTRY ListHead,
+ IN PCI_SIGNATURE ExtensionType)
+{
+ PSINGLE_LIST_ENTRY NextEntry;
+ PPCI_SECONDARY_EXTENSION Extension;
+
+ /* Scan the list */
+ for (NextEntry = ListHead; NextEntry; NextEntry = NextEntry->Next)
+ {
+ /* Grab each extension and check if it's the one requested */
+ Extension = CONTAINING_RECORD(NextEntry, PCI_SECONDARY_EXTENSION, List);
+ if (Extension->ExtensionType == ExtensionType) return Extension;
+ }
+
+ /* Nothing was found */
+ return NULL;
+}
+
+ULONGLONG
+NTAPI
+PciGetHackFlags(IN USHORT VendorId,
+ IN USHORT DeviceId,
+ IN USHORT SubVendorId,
+ IN USHORT SubSystemId,
+ IN UCHAR RevisionId)
+{
+ PPCI_HACK_ENTRY HackEntry;
+ ULONGLONG HackFlags;
+ ULONG LastWeight, MatchWeight;
+ ULONG EntryFlags;
+
+ /* Initialize the variables before looping */
+ LastWeight = 0;
+ HackFlags = 0;
+ ASSERT(PciHackTable);
+
+ /* Scan the hack table */
+ for (HackEntry = PciHackTable;
+ HackEntry->VendorID != PCI_INVALID_VENDORID;
+ ++HackEntry)
+ {
+ /* Check if there's an entry for this device */
+ if ((HackEntry->DeviceID == DeviceId) &&
+ (HackEntry->VendorID == VendorId))
+ {
+ /* This is a basic match */
+ EntryFlags = HackEntry->Flags;
+ MatchWeight = 1;
+
+ /* Does the entry have revision information? */
+ if (EntryFlags & PCI_HACK_HAS_REVISION_INFO)
+ {
+ /* Check if the revision matches, if so, this is a better match */
+ if (HackEntry->RevisionID != RevisionId) continue;
+ MatchWeight = 3;
+ }
+
+ /* Does the netry have subsystem information? */
+ if (EntryFlags & PCI_HACK_HAS_SUBSYSTEM_INFO)
+ {
+ /* Check if it matches, if so, this is the best possible match */
+ if ((HackEntry->SubVendorID != SubVendorId) ||
+ (HackEntry->SubSystemID != SubSystemId))
+ {
+ continue;
+ }
+ MatchWeight += 4;
+ }
+
+ /* Is this the best match yet? */
+ if (MatchWeight > LastWeight)
+ {
+ /* This is the best match for now, use this as the hack flags */
+ HackFlags = HackEntry->HackFlags;
+ LastWeight = MatchWeight;
+ }
+ }
+ }
+
+ /* Return the best match */
+ return HackFlags;
+}
+
+BOOLEAN
+NTAPI
+PciIsCriticalDeviceClass(IN UCHAR BaseClass,
+ IN UCHAR SubClass)
+{
+ /* Check for system or bridge devices */
+ if (BaseClass == PCI_CLASS_BASE_SYSTEM_DEV)
+ {
+ /* Interrupt controlers are critical */
+ return SubClass == PCI_SUBCLASS_SYS_INTERRUPT_CTLR;
+ }
+ else if (BaseClass == PCI_CLASS_BRIDGE_DEV)
+ {
+ /* ISA Bridges are critical */
+ return SubClass == PCI_SUBCLASS_BR_ISA;
+ }
+ else
+ {
+ /* All display controllers are critical */
+ return BaseClass == PCI_CLASS_DISPLAY_CTLR;
+ }
+}
+
+PPCI_PDO_EXTENSION
+NTAPI
+PciFindPdoByFunction(IN PPCI_FDO_EXTENSION DeviceExtension,
+ IN ULONG FunctionNumber,
+ IN PPCI_COMMON_HEADER PciData)
+{
+ KIRQL Irql;
+ PPCI_PDO_EXTENSION PdoExtension;
+
+ /* Get the current IRQL when this call was made */
+ Irql = KeGetCurrentIrql();
+
+ /* Is this a low-IRQL call? */
+ if (Irql < DISPATCH_LEVEL)
+ {
+ /* Acquire this device's lock */
+ KeEnterCriticalRegion();
+ KeWaitForSingleObject(&DeviceExtension->ChildListLock,
+ Executive,
+ KernelMode,
+ FALSE,
+ NULL);
+ }
+
+ /* Loop every child PDO */
+ for (PdoExtension = DeviceExtension->ChildPdoList;
+ PdoExtension;
+ PdoExtension = PdoExtension->Next)
+ {
+ /* Find only enumerated PDOs */
+ if (!PdoExtension->ReportedMissing)
+ {
+ /* Check if the function number and header data matches */
+ if ((FunctionNumber == PdoExtension->Slot.u.AsULONG) &&
+ (PdoExtension->VendorId == PciData->VendorID) &&
+ (PdoExtension->DeviceId == PciData->DeviceID) &&
+ (PdoExtension->RevisionId == PciData->RevisionID))
+ {
+ /* This is considered to be the same PDO */
+ break;
+ }
+ }
+ }
+
+ /* Was this a low-IRQL call? */
+ if (Irql < DISPATCH_LEVEL)
+ {
+ /* Release this device's lock */
+ KeSetEvent(&DeviceExtension->ChildListLock, IO_NO_INCREMENT, FALSE);
+ KeLeaveCriticalRegion();
+ }
+
+ /* If the search found something, this is non-NULL, otherwise it's NULL */
+ return PdoExtension;
+}
+
+BOOLEAN
+NTAPI
+PciIsDeviceOnDebugPath(IN PPCI_PDO_EXTENSION DeviceExtension)
+{
+ PAGED_CODE();
+
+ /* Check for too many, or no, debug ports */
+ ASSERT(PciDebugPortsCount <= MAX_DEBUGGING_DEVICES_SUPPORTED);
+ if (!PciDebugPortsCount) return FALSE;
+
+ /* eVb has not been able to test such devices yet */
+ UNIMPLEMENTED;
+ while (TRUE);
+}
+
+NTSTATUS
+NTAPI
+PciGetBiosConfig(IN PPCI_PDO_EXTENSION DeviceExtension,
+ OUT PPCI_COMMON_HEADER PciData)
+{
+ HANDLE KeyHandle, SubKeyHandle;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ UNICODE_STRING KeyName, KeyValue;
+ WCHAR Buffer[32];
+ WCHAR DataBuffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + PCI_COMMON_HDR_LENGTH];
+ PKEY_VALUE_PARTIAL_INFORMATION PartialInfo = (PVOID)DataBuffer;
+ NTSTATUS Status;
+ ULONG ResultLength;
+ PAGED_CODE();
+
+ /* Open the PCI key */
+ Status = IoOpenDeviceRegistryKey(DeviceExtension->ParentFdoExtension->
+ PhysicalDeviceObject,
+ TRUE,
+ KEY_ALL_ACCESS,
+ &KeyHandle);
+ if (!NT_SUCCESS(Status)) return Status;
+
+ /* Create a volatile BIOS configuration key */
+ RtlInitUnicodeString(&KeyName, L"BiosConfig");
+ InitializeObjectAttributes(&ObjectAttributes,
+ &KeyName,
+ OBJ_KERNEL_HANDLE,
+ KeyHandle,
+ NULL);
+ Status = ZwCreateKey(&SubKeyHandle,
+ KEY_READ,
+ &ObjectAttributes,
+ 0,
+ NULL,
+ REG_OPTION_VOLATILE,
+ NULL);
+ ZwClose(KeyHandle);
+ if (!NT_SUCCESS(Status)) return Status;
+
+ /* Create the key value based on the device and function number */
+ swprintf(Buffer,
+ L"DEV_%02x&FUN_%02x",
+ DeviceExtension->Slot.u.bits.DeviceNumber,
+ DeviceExtension->Slot.u.bits.FunctionNumber);
+ RtlInitUnicodeString(&KeyValue, Buffer);
+
+ /* Query the value information (PCI BIOS configuration header) */
+ Status = ZwQueryValueKey(SubKeyHandle,
+ &KeyValue,
+ KeyValuePartialInformation,
+ PartialInfo,
+ sizeof(DataBuffer),
+ &ResultLength);
+ ZwClose(SubKeyHandle);
+ if (!NT_SUCCESS(Status)) return Status;
+
+ /* If any information was returned, go ahead and copy its data */
+ ASSERT(PartialInfo->DataLength == PCI_COMMON_HDR_LENGTH);
+ RtlCopyMemory(PciData, PartialInfo->Data, PCI_COMMON_HDR_LENGTH);
+ return Status;
+}
+
+NTSTATUS
+NTAPI
+PciSaveBiosConfig(IN PPCI_PDO_EXTENSION DeviceExtension,
+ IN PPCI_COMMON_HEADER PciData)
+{
+ HANDLE KeyHandle, SubKeyHandle;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ UNICODE_STRING KeyName, KeyValue;
+ WCHAR Buffer[32];
+ NTSTATUS Status;
+ PAGED_CODE();
+
+ /* Open the PCI key */
+ Status = IoOpenDeviceRegistryKey(DeviceExtension->ParentFdoExtension->
+ PhysicalDeviceObject,
+ TRUE,
+ KEY_READ | KEY_WRITE,
+ &KeyHandle);
+ if (!NT_SUCCESS(Status)) return Status;
+
+ /* Create a volatile BIOS configuration key */
+ RtlInitUnicodeString(&KeyName, L"BiosConfig");
+ InitializeObjectAttributes(&ObjectAttributes,
+ &KeyName,
+ OBJ_KERNEL_HANDLE,
+ KeyHandle,
+ NULL);
+ Status = ZwCreateKey(&SubKeyHandle,
+ KEY_READ | KEY_WRITE,
+ &ObjectAttributes,
+ 0,
+ NULL,
+ REG_OPTION_VOLATILE,
+ NULL);
+ ZwClose(KeyHandle);
+ if (!NT_SUCCESS(Status)) return Status;
+
+ /* Create the key value based on the device and function number */
+ swprintf(Buffer,
+ L"DEV_%02x&FUN_%02x",
+ DeviceExtension->Slot.u.bits.DeviceNumber,
+ DeviceExtension->Slot.u.bits.FunctionNumber);
+ RtlInitUnicodeString(&KeyValue, Buffer);
+
+ /* Set the value data (the PCI BIOS configuration header) */
+ Status = ZwSetValueKey(SubKeyHandle,
+ &KeyValue,
+ 0,
+ REG_BINARY,
+ PciData,
+ PCI_COMMON_HDR_LENGTH);
+ ZwClose(SubKeyHandle);
+ return Status;
+}
+
+UCHAR
+NTAPI
+PciReadDeviceCapability(IN PPCI_PDO_EXTENSION DeviceExtension,
+ IN UCHAR Offset,
+ IN ULONG CapabilityId,
+ OUT PPCI_CAPABILITIES_HEADER Buffer,
+ IN ULONG Length)
+{
+ ULONG CapabilityCount = 0;
+
+ /* If the device has no capabilility list, fail */
+ if (!Offset) return 0;
+
+ /* Validate a PDO with capabilities, a valid buffer, and a valid length */
+ ASSERT(DeviceExtension->ExtensionType == PciPdoExtensionType);
+ ASSERT(DeviceExtension->CapabilitiesPtr != 0);
+ ASSERT(Buffer);
+ ASSERT(Length >= sizeof(PCI_CAPABILITIES_HEADER));
+
+ /* Loop all capabilities */
+ while (Offset)
+ {
+ /* Make sure the pointer is spec-aligned and spec-sized */
+ ASSERT((Offset >= PCI_COMMON_HDR_LENGTH) && ((Offset & 0x3) == 0));
+
+ /* Read the capability header */
+ PciReadDeviceConfig(DeviceExtension,
+ Buffer,
+ Offset,
+ sizeof(PCI_CAPABILITIES_HEADER));
+
+ /* Check if this is the capability being looked up */
+ if ((Buffer->CapabilityID == CapabilityId) || !(CapabilityId))
+ {
+ /* Check if was at a valid offset and length */
+ if ((Offset) && (Length > sizeof(PCI_CAPABILITIES_HEADER)))
+ {
+ /* Sanity check */
+ ASSERT(Length <= (sizeof(PCI_COMMON_CONFIG) - Offset));
+
+ /* Now read the whole capability data into the buffer */
+ PciReadDeviceConfig(DeviceExtension,
+ (PVOID)((ULONG_PTR)Buffer +
+ sizeof(PCI_CAPABILITIES_HEADER)),
+ Offset + sizeof(PCI_CAPABILITIES_HEADER),
+ Length - sizeof(PCI_CAPABILITIES_HEADER));
+ }
+
+ /* Return the offset where the capability was found */
+ return Offset;
+ }
+
+ /* Try the next capability instead */
+ CapabilityCount++;
+ Offset = Buffer->Next;
+
+ /* There can't be more than 48 capabilities (256 bytes max) */
+ if (CapabilityCount > 48)
+ {
+ /* Fail, since this is basically a broken PCI device */
+ DPRINT1("PCI device %p capabilities list is broken.\n", DeviceExtension);
+ return 0;
+ }
+ }
+
+ /* Capability wasn't found, fail */
+ return 0;
+}
+
+BOOLEAN
+NTAPI
+PciCanDisableDecodes(IN PPCI_PDO_EXTENSION DeviceExtension,
+ IN PPCI_COMMON_HEADER Config,
+ IN ULONGLONG HackFlags,
+ IN BOOLEAN ForPowerDown)
+{
+ UCHAR BaseClass, SubClass;
+ BOOLEAN IsVga;
+
+ /* Is there a device extension or should the PCI header be used? */
+ if (DeviceExtension)
+ {
+ /* Never disable decodes for a debug PCI Device */
+ if (DeviceExtension->OnDebugPath) return FALSE;
+
+ /* Hack flags will be obtained from the extension, not the caller */
+ ASSERT(HackFlags == 0);
+
+ /* Get hacks and classification from the device extension */
+ HackFlags = DeviceExtension->HackFlags;
+ SubClass = DeviceExtension->SubClass;
+ BaseClass = DeviceExtension->BaseClass;
+ }
+ else
+ {
+ /* There must be a PCI header, go read the classification information */
+ ASSERT(Config != NULL);
+ BaseClass = Config->BaseClass;
+ SubClass = Config->SubClass;
+ }
+
+ /* Check for hack flags that prevent disabling the decodes */
+ if (HackFlags & (PCI_HACK_PRESERVE_COMMAND |
+ PCI_HACK_CB_SHARE_CMD_BITS |
+ PCI_HACK_DONT_DISABLE_DECODES))
+ {
+ /* Don't do it */
+ return FALSE;
+ }
+
+ /* Is this a VGA adapter? */
+ if ((BaseClass == PCI_CLASS_DISPLAY_CTLR) &&
+ (SubClass == PCI_SUBCLASS_VID_VGA_CTLR))
+ {
+ /* Never disable decodes if this is for power down */
+ return ForPowerDown;
+ }
+
+ /* Check for legacy devices */
+ if (BaseClass == PCI_CLASS_PRE_20)
+ {
+ /* Never disable video adapter cards if this is for power down */
+ if (SubClass == PCI_SUBCLASS_PRE_20_VGA) return ForPowerDown;
+ }
+ else if (BaseClass == PCI_CLASS_DISPLAY_CTLR)
+ {
+ /* Never disable VGA adapters if this is for power down */
+ if (SubClass == PCI_SUBCLASS_VID_VGA_CTLR) return ForPowerDown;
+ }
+ else if (BaseClass == PCI_CLASS_BRIDGE_DEV)
+ {
+ /* Check for legacy bridges */
+ if ((SubClass == PCI_SUBCLASS_BR_ISA) ||
+ (SubClass == PCI_SUBCLASS_BR_EISA) ||
+ (SubClass == PCI_SUBCLASS_BR_MCA) ||
+ (SubClass == PCI_SUBCLASS_BR_HOST) ||
+ (SubClass == PCI_SUBCLASS_BR_OTHER))
+ {
+ /* Never disable these */
+ return FALSE;
+ }
+ else if ((SubClass == PCI_SUBCLASS_BR_PCI_TO_PCI) ||
+ (SubClass == PCI_SUBCLASS_BR_CARDBUS))
+ {
+ /* This is a supported bridge, but does it have a VGA card? */
+ if (!DeviceExtension)
+ {
+ /* Read the bridge control flag from the PCI header */
+ IsVga = Config->u.type1.BridgeControl & PCI_ENABLE_BRIDGE_VGA;
+ }
+ else
+ {
+ /* Read the cached flag in the device extension */
+ IsVga = DeviceExtension->Dependent.type1.VgaBitSet;
+ }
+
+ /* Never disable VGA adapters if this is for power down */
+ if (IsVga) return ForPowerDown;
+ }
+ }
+
+ /* Finally, never disable decodes if there's no power management */
+ return !(HackFlags & PCI_HACK_NO_PM_CAPS);
+}
+
+PCI_DEVICE_TYPES
+NTAPI
+PciClassifyDeviceType(IN PPCI_PDO_EXTENSION PdoExtension)
+{
+ ASSERT(PdoExtension->ExtensionType == PciPdoExtensionType);
+
+ /* Differenriate between devices and bridges */
+ if (PdoExtension->BaseClass != PCI_CLASS_BRIDGE_DEV) return PciTypeDevice;
+
+ /* The PCI Bus driver handles only CardBus and PCI bridges (plus host) */
+ if (PdoExtension->SubClass == PCI_SUBCLASS_BR_HOST) return PciTypeHostBridge;
+ if (PdoExtension->SubClass == PCI_SUBCLASS_BR_PCI_TO_PCI) return PciTypePciBridge;
+ if (PdoExtension->SubClass == PCI_SUBCLASS_BR_CARDBUS) return PciTypeCardbusBridge;
+
+ /* Any other kind of bridge is treated like a device */
+ return PciTypeDevice;
+}
+
+ULONG_PTR
+NTAPI
+PciExecuteCriticalSystemRoutine(IN ULONG_PTR IpiContext)
+{
+ PPCI_IPI_CONTEXT Context = (PPCI_IPI_CONTEXT)IpiContext;
+
+ /* Check if the IPI is already running */
+ if (!InterlockedDecrement(&Context->RunCount))
+ {
+ /* Nope, this is the first instance, so execute the IPI function */
+ Context->Function(Context->PdoExtension, Context->Context);
+
+ /* Notify anyone that was spinning that they can stop now */
+ Context->Barrier = 0;
+ }
+ else
+ {
+ /* Spin until it has finished running */
+ while (Context->Barrier);
+ }
+
+ /* Done */
+ return 0;
+}
+
+BOOLEAN
+NTAPI
+PciIsSlotPresentInParentMethod(IN PPCI_PDO_EXTENSION PdoExtension,
+ IN ULONG Method)
+{
+ BOOLEAN FoundSlot;
+ PACPI_METHOD_ARGUMENT Argument;
+ ACPI_EVAL_INPUT_BUFFER InputBuffer;
+ PACPI_EVAL_OUTPUT_BUFFER OutputBuffer;
+ ULONG i, Length;
+ NTSTATUS Status;
+ PAGED_CODE();
+
+ /* Assume slot is not part of the parent method */
+ FoundSlot = FALSE;
+
+ /* Allocate a 2KB buffer for the method return parameters */
+ Length = sizeof(ACPI_EVAL_OUTPUT_BUFFER) + 2048;
+ OutputBuffer = ExAllocatePoolWithTag(PagedPool, Length, 'BicP');
+ if (OutputBuffer)
+ {
+ /* Clear out the output buffer */
+ RtlZeroMemory(OutputBuffer, Length);
+
+ /* Initialize the input buffer with the method requested */
+ InputBuffer.Signature = 0;
+ *(PULONG)InputBuffer.MethodName = Method;
+ InputBuffer.Signature = ACPI_EVAL_INPUT_BUFFER_SIGNATURE;
+
+ /* Send it to the ACPI driver */
+ Status = PciSendIoctl(PdoExtension->ParentFdoExtension->PhysicalDeviceObject,
+ IOCTL_ACPI_EVAL_METHOD,
+ &InputBuffer,
+ sizeof(ACPI_EVAL_INPUT_BUFFER),
+ OutputBuffer,
+ Length);
+ if (NT_SUCCESS(Status))
+ {
+ /* Scan all output arguments */
+ for (i = 0; i < OutputBuffer->Count; i++)
+ {
+ /* Make sure it's an integer */
+ Argument = &OutputBuffer->Argument[i];
+ if (Argument->Type != ACPI_METHOD_ARGUMENT_INTEGER) continue;
+
+ /* Check if the argument matches this PCI slot structure */
+ if (Argument->Argument == ((PdoExtension->Slot.u.bits.DeviceNumber) |
+ ((PdoExtension->Slot.u.bits.FunctionNumber) << 16)))
+ {
+ /* This slot has been found, return it */
+ FoundSlot = TRUE;
+ break;
+ }
+ }
+ }
+
+ /* Finished with the buffer, free it */
+ ExFreePoolWithTag(OutputBuffer, 0);
+ }
+
+ /* Return if the slot was found */
+ return FoundSlot;
+}
+
+ULONG
+NTAPI
+PciGetLengthFromBar(IN ULONG Bar)
+{
+ ULONG Length;
+
+ /* I/O addresses vs. memory addresses start differently due to alignment */
+ Length = 1 << ((Bar & PCI_ADDRESS_IO_SPACE) ? 2 : 4);
+
+ /* Keep going until a set bit */
+ while (!(Length & Bar) && (Length)) Length <<= 1;
+
+ /* Return the length (might be 0 on 64-bit because it's the low-word) */
+ if ((Bar & PCI_ADDRESS_MEMORY_TYPE_MASK) != PCI_TYPE_64BIT) ASSERT(Length);
+ return Length;
+}
+
+BOOLEAN
+NTAPI
+PciCreateIoDescriptorFromBarLimit(PIO_RESOURCE_DESCRIPTOR ResourceDescriptor,
+ IN PULONG BarArray,
+ IN BOOLEAN Rom)
+{
+ ULONG CurrentBar, BarLength, BarMask;
+ BOOLEAN Is64BitBar = FALSE;
+
+ /* Check if the BAR is nor I/O nor memory */
+ CurrentBar = BarArray[0];
+ if (!(CurrentBar & ~PCI_ADDRESS_IO_SPACE))
+ {
+ /* Fail this descriptor */
+ ResourceDescriptor->Type = CmResourceTypeNull;
+ return FALSE;
+ }
+
+ /* Set default flag and clear high words */
+ ResourceDescriptor->Flags = 0;
+ ResourceDescriptor->u.Generic.MaximumAddress.HighPart = 0;
+ ResourceDescriptor->u.Generic.MinimumAddress.LowPart = 0;
+ ResourceDescriptor->u.Generic.MinimumAddress.HighPart = 0;
+
+ /* Check for ROM Address */
+ if (Rom)
+ {
+ /* Clean up the BAR to get just the address */
+ CurrentBar &= PCI_ADDRESS_ROM_ADDRESS_MASK;
+ if (!CurrentBar)
+ {
+ /* Invalid ar, fail this descriptor */
+ ResourceDescriptor->Type = CmResourceTypeNull;
+ return FALSE;
+ }
+
+ /* ROM Addresses are always read only */
+ ResourceDescriptor->Flags = CM_RESOURCE_MEMORY_READ_ONLY;
+ }
+
+ /* Compute the length, assume it's the alignment for now */
+ BarLength = PciGetLengthFromBar(CurrentBar);
+ ResourceDescriptor->u.Generic.Length = BarLength;
+ ResourceDescriptor->u.Generic.Alignment = BarLength;
+
+ /* Check what kind of BAR this is */
+ if (CurrentBar & PCI_ADDRESS_IO_SPACE)
+ {
+ /* Use correct mask to decode the address */
+ BarMask = PCI_ADDRESS_IO_ADDRESS_MASK;
+
+ /* Set this as an I/O Port descriptor */
+ ResourceDescriptor->Type = CmResourceTypePort;
+ ResourceDescriptor->Flags = CM_RESOURCE_PORT_IO;
+ }
+ else
+ {
+ /* Use correct mask to decode the address */
+ BarMask = PCI_ADDRESS_MEMORY_ADDRESS_MASK;
+
+ /* Set this as a memory descriptor */
+ ResourceDescriptor->Type = CmResourceTypeMemory;
+
+ /* Check if it's 64-bit or 20-bit decode */
+ if ((CurrentBar & PCI_ADDRESS_MEMORY_TYPE_MASK) == PCI_TYPE_64BIT)
+ {
+ /* The next BAR has the high word, read it */
+ ResourceDescriptor->u.Port.MaximumAddress.HighPart = BarArray[1];
+ Is64BitBar = TRUE;
+ }
+ else if ((CurrentBar & PCI_ADDRESS_MEMORY_TYPE_MASK) == PCI_TYPE_20BIT)
+ {
+ /* Use the correct mask to decode the address */
+ BarMask = ~0xFFF0000F;
+ }
+
+ /* Check if the BAR is listed as prefetchable memory */
+ if (CurrentBar & PCI_ADDRESS_MEMORY_PREFETCHABLE)
+ {
+ /* Mark the descriptor in the same way */
+ ResourceDescriptor->Flags |= CM_RESOURCE_MEMORY_PREFETCHABLE;
+ }
+ }
+
+ /* Now write down the maximum address based on the base + length */
+ ResourceDescriptor->u.Port.MaximumAddress.QuadPart = (CurrentBar & BarMask) +
+ BarLength - 1;
+
+ /* Return if this is a 64-bit BAR, so the loop code knows to skip the next one */
+ return Is64BitBar;
+}
+
+VOID
+NTAPI
+PciDecodeEnable(IN PPCI_PDO_EXTENSION PdoExtension,
+ IN BOOLEAN Enable,
+ OUT PUSHORT Command)
+{
+ USHORT CommandValue;
+
+ /*
+ * If decodes are being disabled, make sure it's allowed, and in both cases,
+ * make sure that a hackflag isn't preventing touching the decodes at all.
+ */
+ if (((Enable) || (PciCanDisableDecodes(PdoExtension, 0, 0, 0))) &&
+ !(PdoExtension->HackFlags & PCI_HACK_PRESERVE_COMMAND))
+ {
+ /* Did the caller already have a command word? */
+ if (Command)
+ {
+ /* Use the caller's */
+ CommandValue = *Command;
+ }
+ else
+ {
+ /* Otherwise, read the current command */
+ PciReadDeviceConfig(PdoExtension,
+ &Command,
+ FIELD_OFFSET(PCI_COMMON_HEADER, Command),
+ sizeof(USHORT));
+ }
+
+ /* Turn off decodes by default */
+ CommandValue &= ~(PCI_ENABLE_IO_SPACE |
+ PCI_ENABLE_MEMORY_SPACE |
+ PCI_ENABLE_BUS_MASTER);
+
+ /* If requested, enable the decodes that were enabled at init time */
+ if (Enable) CommandValue |= PdoExtension->CommandEnables &
+ (PCI_ENABLE_IO_SPACE |
+ PCI_ENABLE_MEMORY_SPACE |
+ PCI_ENABLE_BUS_MASTER);
+
+ /* Update the command word */
+ PciWriteDeviceConfig(PdoExtension,
+ &CommandValue,
+ FIELD_OFFSET(PCI_COMMON_HEADER, Command),
+ sizeof(USHORT));
+ }
+}
+
/* EOF */