2 * PROJECT: ReactOS PCI Bus Driver
3 * LICENSE: BSD - See COPYING.ARM in the top level directory
4 * FILE: drivers/bus/pci/utils.c
5 * PURPOSE: Utility/Helper Support Code
6 * PROGRAMMERS: ReactOS Portable Systems Group
9 /* INCLUDES *******************************************************************/
15 /* GLOBALS ********************************************************************/
17 ULONG PciDebugPortsCount
;
19 RTL_RANGE_LIST PciIsaBitExclusionList
;
20 RTL_RANGE_LIST PciVgaAndIsaBitExclusionList
;
22 /* FUNCTIONS ******************************************************************/
26 PciUnicodeStringStrStr(IN PUNICODE_STRING InputString
,
27 IN PCUNICODE_STRING EqualString
,
28 IN BOOLEAN CaseInSensitive
)
30 UNICODE_STRING PartialString
;
31 LONG EqualChars
, TotalChars
;
33 /* Build a partial string with the smaller substring */
34 PartialString
.Length
= EqualString
->Length
;
35 PartialString
.MaximumLength
= InputString
->MaximumLength
;;
36 PartialString
.Buffer
= InputString
->Buffer
;
38 /* Check how many characters that need comparing */
40 TotalChars
= (InputString
->Length
- EqualString
->Length
) / sizeof(WCHAR
);
42 /* If the substring is bigger, just fail immediately */
43 if (TotalChars
< 0) return FALSE
;
45 /* Keep checking each character */
46 while (!RtlEqualUnicodeString(EqualString
, &PartialString
, CaseInSensitive
))
48 /* Continue checking until all the required characters are equal */
49 PartialString
.Buffer
++;
50 PartialString
.MaximumLength
-= sizeof(WCHAR
);
51 if (++EqualChars
> TotalChars
) return FALSE
;
54 /* The string is equal */
60 PciStringToUSHORT(IN PWCHAR String
,
64 ULONG Low
, High
, Length
;
67 /* Initialize everything to zero */
72 /* Get the character and set the high byte based on the previous one */
76 /* Check for numbers */
77 if ( Char
>= '0' && Char
<= '9' )
79 /* Convert them to a byte */
82 else if ( Char
>= 'A' && Char
<= 'F' )
84 /* Convert upper-case hex letters into a byte */
87 else if ( Char
>= 'a' && Char
<= 'f' )
89 /* Convert lower-case hex letters into a byte */
94 /* Invalid string, fail the conversion */
98 /* Combine the high and low byte */
101 /* If 4 letters have been reached, the 16-bit integer should exist */
104 /* Return it to the caller */
113 PciIsSuiteVersion(IN USHORT SuiteMask
)
116 RTL_OSVERSIONINFOEXW VersionInfo
;
118 /* Initialize the version information */
119 RtlZeroMemory(&VersionInfo
, sizeof(RTL_OSVERSIONINFOEXW
));
120 VersionInfo
.dwOSVersionInfoSize
= sizeof(RTL_OSVERSIONINFOEXW
);
121 VersionInfo
.wSuiteMask
= SuiteMask
;
123 /* Set the comparison mask and return if the passed suite mask matches */
124 VER_SET_CONDITION(Mask
, VER_SUITENAME
, VER_AND
);
125 return NT_SUCCESS(RtlVerifyVersionInfo(&VersionInfo
, VER_SUITENAME
, Mask
));
130 PciIsDatacenter(VOID
)
137 /* Assume this isn't Datacenter */
140 /* First, try opening the setup key */
141 Status
= PciGetRegistryValue(L
"",
142 L
"\\REGISTRY\\MACHINE\\SYSTEM\\CurrentControlSet\\Services\\setupdd",
147 if (!NT_SUCCESS(Status
))
149 /* This is not an in-progress Setup boot, so query the suite version */
150 Result
= PciIsSuiteVersion(VER_SUITE_DATACENTER
);
154 /* This scenario shouldn't happen yet, since SetupDD isn't used */
159 /* Return if this is Datacenter or not */
165 PciOpenKey(IN PWCHAR KeyName
,
167 IN ACCESS_MASK DesiredAccess
,
168 OUT PHANDLE KeyHandle
,
169 OUT PNTSTATUS KeyStatus
)
172 OBJECT_ATTRIBUTES ObjectAttributes
;
173 UNICODE_STRING KeyString
;
176 /* Initialize the object attributes */
177 RtlInitUnicodeString(&KeyString
, KeyName
);
178 InitializeObjectAttributes(&ObjectAttributes
,
180 OBJ_CASE_INSENSITIVE
,
184 /* Open the key, returning a boolean, and the status, if requested */
185 Status
= ZwOpenKey(KeyHandle
, DesiredAccess
, &ObjectAttributes
);
186 if (KeyStatus
) *KeyStatus
= Status
;
187 return NT_SUCCESS(Status
);
192 PciGetRegistryValue(IN PWCHAR ValueName
,
194 IN HANDLE RootHandle
,
196 OUT PVOID
*OutputBuffer
,
197 OUT PULONG OutputLength
)
200 PKEY_VALUE_PARTIAL_INFORMATION PartialInfo
;
201 ULONG NeededLength
, ActualLength
;
202 UNICODE_STRING ValueString
;
206 /* So we know what to free at the end of the body */
211 /* Open the key by name, rooted off the handle passed */
212 Result
= PciOpenKey(KeyName
,
219 /* Query for the size that's needed for the value that was passed in */
220 RtlInitUnicodeString(&ValueString
, ValueName
);
221 Status
= ZwQueryValueKey(KeyHandle
,
223 KeyValuePartialInformation
,
227 ASSERT(!NT_SUCCESS(Status
));
228 if (Status
!= STATUS_BUFFER_TOO_SMALL
) break;
230 /* Allocate an appropriate buffer for the size that was returned */
231 ASSERT(NeededLength
!= 0);
232 Status
= STATUS_INSUFFICIENT_RESOURCES
;
233 PartialInfo
= ExAllocatePoolWithTag(PagedPool
,
236 if (!PartialInfo
) break;
238 /* Query the actual value information now that the size is known */
239 Status
= ZwQueryValueKey(KeyHandle
,
241 KeyValuePartialInformation
,
245 if (!NT_SUCCESS(Status
)) break;
247 /* Make sure it's of the type that the caller expects */
248 Status
= STATUS_INVALID_PARAMETER
;
249 if (PartialInfo
->Type
!= Type
) break;
251 /* Subtract the registry-specific header, to get the data size */
252 ASSERT(NeededLength
== ActualLength
);
253 NeededLength
-= sizeof(KEY_VALUE_PARTIAL_INFORMATION
);
255 /* Allocate a buffer to hold the data and return it to the caller */
256 Status
= STATUS_INSUFFICIENT_RESOURCES
;
257 *OutputBuffer
= ExAllocatePoolWithTag(PagedPool
,
260 if (!*OutputBuffer
) break;
262 /* Copy the data into the buffer and return its length to the caller */
263 RtlCopyMemory(*OutputBuffer
, PartialInfo
->Data
, NeededLength
);
264 if (OutputLength
) *OutputLength
= NeededLength
;
265 Status
= STATUS_SUCCESS
;
268 /* Close any opened keys and free temporary allocations */
269 if (KeyHandle
) ZwClose(KeyHandle
);
270 if (PartialInfo
) ExFreePoolWithTag(PartialInfo
, 0);
276 PciBuildDefaultExclusionLists(VOID
)
280 ASSERT(PciIsaBitExclusionList
.Count
== 0);
281 ASSERT(PciVgaAndIsaBitExclusionList
.Count
== 0);
283 /* Initialize the range lists */
284 RtlInitializeRangeList(&PciIsaBitExclusionList
);
285 RtlInitializeRangeList(&PciVgaAndIsaBitExclusionList
);
287 /* Loop x86 I/O ranges */
288 for (Start
= 0x100; Start
<= 0xFEFF; Start
+= 0x400)
290 /* Add the ISA I/O ranges */
291 Status
= RtlAddRange(&PciIsaBitExclusionList
,
295 RTL_RANGE_LIST_ADD_IF_CONFLICT
,
298 if (!NT_SUCCESS(Status
)) break;
300 /* Add the ISA I/O ranges */
301 Status
= RtlAddRange(&PciVgaAndIsaBitExclusionList
,
305 RTL_RANGE_LIST_ADD_IF_CONFLICT
,
308 if (!NT_SUCCESS(Status
)) break;
310 /* Add the VGA I/O range for Monochrome Video */
311 Status
= RtlAddRange(&PciVgaAndIsaBitExclusionList
,
315 RTL_RANGE_LIST_ADD_IF_CONFLICT
,
318 if (!NT_SUCCESS(Status
)) break;
320 /* Add the VGA I/O range for certain CGA adapters */
321 Status
= RtlAddRange(&PciVgaAndIsaBitExclusionList
,
325 RTL_RANGE_LIST_ADD_IF_CONFLICT
,
328 if (!NT_SUCCESS(Status
)) break;
330 /* Success, ranges added done */
333 RtlFreeRangeList(&PciIsaBitExclusionList
);
334 RtlFreeRangeList(&PciVgaAndIsaBitExclusionList
);
340 PciFindParentPciFdoExtension(IN PDEVICE_OBJECT DeviceObject
,
343 PPCI_FDO_EXTENSION DeviceExtension
;
344 PPCI_PDO_EXTENSION SearchExtension
, FoundExtension
;
346 /* Assume we'll find nothing */
347 SearchExtension
= DeviceObject
->DeviceExtension
;
348 FoundExtension
= NULL
;
350 /* Check if a lock was specified */
353 /* Wait for the lock to be released */
354 KeEnterCriticalRegion();
355 KeWaitForSingleObject(Lock
, Executive
, KernelMode
, FALSE
, NULL
);
358 /* Now search for the extension */
359 DeviceExtension
= (PPCI_FDO_EXTENSION
)PciFdoExtensionListHead
.Next
;
360 while (DeviceExtension
)
362 /* Acquire this device's lock */
363 KeEnterCriticalRegion();
364 KeWaitForSingleObject(&DeviceExtension
->ChildListLock
,
370 /* Scan all children PDO, stop when no more PDOs, or found it */
371 for (FoundExtension
= DeviceExtension
->ChildPdoList
;
372 FoundExtension
&& (FoundExtension
!= SearchExtension
);
373 FoundExtension
= FoundExtension
->Next
);
375 /* If we found it, break out */
376 if (FoundExtension
) break;
378 /* Release this device's lock */
379 KeSetEvent(&DeviceExtension
->ChildListLock
, IO_NO_INCREMENT
, FALSE
);
380 KeLeaveCriticalRegion();
382 /* Move to the next device */
383 DeviceExtension
= (PPCI_FDO_EXTENSION
)DeviceExtension
->List
.Next
;
386 /* Check if we had acquired a lock previously */
390 KeSetEvent(Lock
, IO_NO_INCREMENT
, FALSE
);
391 KeLeaveCriticalRegion();
394 /* Return which extension was found, if any */
395 return DeviceExtension
;
400 PciInsertEntryAtTail(IN PSINGLE_LIST_ENTRY ListHead
,
401 IN PPCI_FDO_EXTENSION DeviceExtension
,
404 PSINGLE_LIST_ENTRY NextEntry
;
407 /* Check if a lock was specified */
410 /* Wait for the lock to be released */
411 KeEnterCriticalRegion();
412 KeWaitForSingleObject(Lock
, Executive
, KernelMode
, FALSE
, NULL
);
415 /* Loop the list until we get to the end, then insert this entry there */
416 for (NextEntry
= ListHead
; NextEntry
->Next
; NextEntry
= NextEntry
->Next
);
417 NextEntry
->Next
= &DeviceExtension
->List
;
419 /* Check if we had acquired a lock previously */
423 KeSetEvent(Lock
, IO_NO_INCREMENT
, FALSE
);
424 KeLeaveCriticalRegion();
430 PciInsertEntryAtHead(IN PSINGLE_LIST_ENTRY ListHead
,
431 IN PSINGLE_LIST_ENTRY Entry
,
436 /* Check if a lock was specified */
439 /* Wait for the lock to be released */
440 KeEnterCriticalRegion();
441 KeWaitForSingleObject(Lock
, Executive
, KernelMode
, FALSE
, NULL
);
444 /* Make the entry point to the current head and make the head point to it */
445 Entry
->Next
= ListHead
->Next
;
446 ListHead
->Next
= Entry
;
448 /* Check if we had acquired a lock previously */
452 KeSetEvent(Lock
, IO_NO_INCREMENT
, FALSE
);
453 KeLeaveCriticalRegion();
459 PcipLinkSecondaryExtension(IN PSINGLE_LIST_ENTRY List
,
461 IN PPCI_SECONDARY_EXTENSION SecondaryExtension
,
462 IN PCI_SIGNATURE ExtensionType
,
467 /* Setup the extension data, and insert it into the primary's list */
468 SecondaryExtension
->ExtensionType
= ExtensionType
;
469 SecondaryExtension
->Destructor
= Destructor
;
470 PciInsertEntryAtHead(List
, &SecondaryExtension
->List
, Lock
);
475 PciGetDeviceProperty(IN PDEVICE_OBJECT DeviceObject
,
476 IN DEVICE_REGISTRY_PROPERTY DeviceProperty
,
477 OUT PVOID
*OutputBuffer
)
480 ULONG BufferLength
, ResultLength
;
484 /* Query the requested property size */
485 Status
= IoGetDeviceProperty(DeviceObject
,
490 if (Status
!= STATUS_BUFFER_TOO_SMALL
)
492 /* Call should've failed with buffer too small! */
493 DPRINT1("PCI - Unexpected status from GetDeviceProperty, saw %08X, expected %08X.\n",
495 STATUS_BUFFER_TOO_SMALL
);
496 *OutputBuffer
= NULL
;
497 ASSERTMSG(FALSE
, "PCI Successfully did the impossible!");
501 /* Allocate the required buffer */
502 Buffer
= ExAllocatePoolWithTag(PagedPool
, BufferLength
, 'BicP');
505 /* No memory, fail the request */
506 DPRINT1("PCI - Failed to allocate DeviceProperty buffer (%d bytes).\n", BufferLength
);
507 Status
= STATUS_INSUFFICIENT_RESOURCES
;
511 /* Do the actual property query call */
512 Status
= IoGetDeviceProperty(DeviceObject
,
517 if (!NT_SUCCESS(Status
)) break;
519 /* Return the buffer to the caller */
520 ASSERT(BufferLength
== ResultLength
);
521 *OutputBuffer
= Buffer
;
522 return STATUS_SUCCESS
;
526 return STATUS_UNSUCCESSFUL
;
531 PciSendIoctl(IN PDEVICE_OBJECT DeviceObject
,
532 IN ULONG IoControlCode
,
533 IN PVOID InputBuffer
,
534 IN ULONG InputBufferLength
,
535 IN PVOID OutputBuffer
,
536 IN ULONG OutputBufferLength
)
541 IO_STATUS_BLOCK IoStatusBlock
;
542 PDEVICE_OBJECT AttachedDevice
;
545 /* Initialize the pending IRP event */
546 KeInitializeEvent(&Event
, SynchronizationEvent
, FALSE
);
548 /* Get a reference to the root PDO (ACPI) */
549 AttachedDevice
= IoGetAttachedDeviceReference(DeviceObject
);
550 if (!AttachedDevice
) return STATUS_INVALID_PARAMETER
;
552 /* Build the requested IOCTL IRP */
553 Irp
= IoBuildDeviceIoControlRequest(IoControlCode
,
562 if (!Irp
) return STATUS_INSUFFICIENT_RESOURCES
;
564 /* Send the IOCTL to the driver */
565 Status
= IoCallDriver(AttachedDevice
, Irp
);
566 if (Status
== STATUS_PENDING
)
568 /* Wait for a response */
569 KeWaitForSingleObject(&Event
,
574 Status
= Irp
->IoStatus
.Status
;
577 /* Take away the reference we took and return the result to the caller */
578 ObDereferenceObject(AttachedDevice
);
582 PPCI_SECONDARY_EXTENSION
584 PciFindNextSecondaryExtension(IN PSINGLE_LIST_ENTRY ListHead
,
585 IN PCI_SIGNATURE ExtensionType
)
587 PSINGLE_LIST_ENTRY NextEntry
;
588 PPCI_SECONDARY_EXTENSION Extension
;
591 for (NextEntry
= ListHead
; NextEntry
; NextEntry
= NextEntry
->Next
)
593 /* Grab each extension and check if it's the one requested */
594 Extension
= CONTAINING_RECORD(NextEntry
, PCI_SECONDARY_EXTENSION
, List
);
595 if (Extension
->ExtensionType
== ExtensionType
) return Extension
;
598 /* Nothing was found */
604 PciGetHackFlags(IN USHORT VendorId
,
606 IN USHORT SubVendorId
,
607 IN USHORT SubSystemId
,
610 PPCI_HACK_ENTRY HackEntry
;
612 ULONG LastWeight
, MatchWeight
;
615 /* Initialize the variables before looping */
618 ASSERT(PciHackTable
);
620 /* Scan the hack table */
621 for (HackEntry
= PciHackTable
;
622 HackEntry
->VendorID
!= PCI_INVALID_VENDORID
;
625 /* Check if there's an entry for this device */
626 if ((HackEntry
->DeviceID
== DeviceId
) &&
627 (HackEntry
->VendorID
== VendorId
))
629 /* This is a basic match */
630 EntryFlags
= HackEntry
->Flags
;
633 /* Does the entry have revision information? */
634 if (EntryFlags
& PCI_HACK_HAS_REVISION_INFO
)
636 /* Check if the revision matches, if so, this is a better match */
637 if (HackEntry
->RevisionID
!= RevisionId
) continue;
641 /* Does the netry have subsystem information? */
642 if (EntryFlags
& PCI_HACK_HAS_SUBSYSTEM_INFO
)
644 /* Check if it matches, if so, this is the best possible match */
645 if ((HackEntry
->SubVendorID
!= SubVendorId
) ||
646 (HackEntry
->SubSystemID
!= SubSystemId
))
653 /* Is this the best match yet? */
654 if (MatchWeight
> LastWeight
)
656 /* This is the best match for now, use this as the hack flags */
657 HackFlags
= HackEntry
->HackFlags
;
658 LastWeight
= MatchWeight
;
663 /* Return the best match */
669 PciIsCriticalDeviceClass(IN UCHAR BaseClass
,
672 /* Check for system or bridge devices */
673 if (BaseClass
== PCI_CLASS_BASE_SYSTEM_DEV
)
675 /* Interrupt controlers are critical */
676 return SubClass
== PCI_SUBCLASS_SYS_INTERRUPT_CTLR
;
678 else if (BaseClass
== PCI_CLASS_BRIDGE_DEV
)
680 /* ISA Bridges are critical */
681 return SubClass
== PCI_SUBCLASS_BR_ISA
;
685 /* All display controllers are critical */
686 return BaseClass
== PCI_CLASS_DISPLAY_CTLR
;
692 PciFindPdoByFunction(IN PPCI_FDO_EXTENSION DeviceExtension
,
693 IN ULONG FunctionNumber
,
694 IN PPCI_COMMON_HEADER PciData
)
697 PPCI_PDO_EXTENSION PdoExtension
;
699 /* Get the current IRQL when this call was made */
700 Irql
= KeGetCurrentIrql();
702 /* Is this a low-IRQL call? */
703 if (Irql
< DISPATCH_LEVEL
)
705 /* Acquire this device's lock */
706 KeEnterCriticalRegion();
707 KeWaitForSingleObject(&DeviceExtension
->ChildListLock
,
714 /* Loop every child PDO */
715 for (PdoExtension
= DeviceExtension
->ChildPdoList
;
717 PdoExtension
= PdoExtension
->Next
)
719 /* Find only enumerated PDOs */
720 if (!PdoExtension
->ReportedMissing
)
722 /* Check if the function number and header data matches */
723 if ((FunctionNumber
== PdoExtension
->Slot
.u
.AsULONG
) &&
724 (PdoExtension
->VendorId
== PciData
->VendorID
) &&
725 (PdoExtension
->DeviceId
== PciData
->DeviceID
) &&
726 (PdoExtension
->RevisionId
== PciData
->RevisionID
))
728 /* This is considered to be the same PDO */
734 /* Was this a low-IRQL call? */
735 if (Irql
< DISPATCH_LEVEL
)
737 /* Release this device's lock */
738 KeSetEvent(&DeviceExtension
->ChildListLock
, IO_NO_INCREMENT
, FALSE
);
739 KeLeaveCriticalRegion();
742 /* If the search found something, this is non-NULL, otherwise it's NULL */
748 PciIsDeviceOnDebugPath(IN PPCI_PDO_EXTENSION DeviceExtension
)
752 /* Check for too many, or no, debug ports */
753 ASSERT(PciDebugPortsCount
<= MAX_DEBUGGING_DEVICES_SUPPORTED
);
754 if (!PciDebugPortsCount
) return FALSE
;
756 /* eVb has not been able to test such devices yet */
763 PciGetBiosConfig(IN PPCI_PDO_EXTENSION DeviceExtension
,
764 OUT PPCI_COMMON_HEADER PciData
)
766 HANDLE KeyHandle
, SubKeyHandle
;
767 OBJECT_ATTRIBUTES ObjectAttributes
;
768 UNICODE_STRING KeyName
, KeyValue
;
770 WCHAR DataBuffer
[sizeof(KEY_VALUE_PARTIAL_INFORMATION
) + PCI_COMMON_HDR_LENGTH
];
771 PKEY_VALUE_PARTIAL_INFORMATION PartialInfo
= (PVOID
)DataBuffer
;
776 /* Open the PCI key */
777 Status
= IoOpenDeviceRegistryKey(DeviceExtension
->ParentFdoExtension
->
778 PhysicalDeviceObject
,
782 if (!NT_SUCCESS(Status
)) return Status
;
784 /* Create a volatile BIOS configuration key */
785 RtlInitUnicodeString(&KeyName
, L
"BiosConfig");
786 InitializeObjectAttributes(&ObjectAttributes
,
791 Status
= ZwCreateKey(&SubKeyHandle
,
799 if (!NT_SUCCESS(Status
)) return Status
;
801 /* Create the key value based on the device and function number */
803 L
"DEV_%02x&FUN_%02x",
804 DeviceExtension
->Slot
.u
.bits
.DeviceNumber
,
805 DeviceExtension
->Slot
.u
.bits
.FunctionNumber
);
806 RtlInitUnicodeString(&KeyValue
, Buffer
);
808 /* Query the value information (PCI BIOS configuration header) */
809 Status
= ZwQueryValueKey(SubKeyHandle
,
811 KeyValuePartialInformation
,
815 ZwClose(SubKeyHandle
);
816 if (!NT_SUCCESS(Status
)) return Status
;
818 /* If any information was returned, go ahead and copy its data */
819 ASSERT(PartialInfo
->DataLength
== PCI_COMMON_HDR_LENGTH
);
820 RtlCopyMemory(PciData
, PartialInfo
->Data
, PCI_COMMON_HDR_LENGTH
);
826 PciSaveBiosConfig(IN PPCI_PDO_EXTENSION DeviceExtension
,
827 IN PPCI_COMMON_HEADER PciData
)
829 HANDLE KeyHandle
, SubKeyHandle
;
830 OBJECT_ATTRIBUTES ObjectAttributes
;
831 UNICODE_STRING KeyName
, KeyValue
;
836 /* Open the PCI key */
837 Status
= IoOpenDeviceRegistryKey(DeviceExtension
->ParentFdoExtension
->
838 PhysicalDeviceObject
,
840 KEY_READ
| KEY_WRITE
,
842 if (!NT_SUCCESS(Status
)) return Status
;
844 /* Create a volatile BIOS configuration key */
845 RtlInitUnicodeString(&KeyName
, L
"BiosConfig");
846 InitializeObjectAttributes(&ObjectAttributes
,
851 Status
= ZwCreateKey(&SubKeyHandle
,
852 KEY_READ
| KEY_WRITE
,
859 if (!NT_SUCCESS(Status
)) return Status
;
861 /* Create the key value based on the device and function number */
863 L
"DEV_%02x&FUN_%02x",
864 DeviceExtension
->Slot
.u
.bits
.DeviceNumber
,
865 DeviceExtension
->Slot
.u
.bits
.FunctionNumber
);
866 RtlInitUnicodeString(&KeyValue
, Buffer
);
868 /* Set the value data (the PCI BIOS configuration header) */
869 Status
= ZwSetValueKey(SubKeyHandle
,
874 PCI_COMMON_HDR_LENGTH
);
875 ZwClose(SubKeyHandle
);