xref: /reactos/drivers/bus/pcix/utils.c (revision 1734f297)
1 /*
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
7  */
8 
9 /* INCLUDES *******************************************************************/
10 
11 #include <pci.h>
12 
13 #define NDEBUG
14 #include <debug.h>
15 
16 /* GLOBALS ********************************************************************/
17 
18 ULONG PciDebugPortsCount;
19 
20 RTL_RANGE_LIST PciIsaBitExclusionList;
21 RTL_RANGE_LIST PciVgaAndIsaBitExclusionList;
22 
23 /* FUNCTIONS ******************************************************************/
24 
25 BOOLEAN
26 NTAPI
27 PciUnicodeStringStrStr(IN PUNICODE_STRING InputString,
28                        IN PCUNICODE_STRING EqualString,
29                        IN BOOLEAN CaseInSensitive)
30 {
31     UNICODE_STRING PartialString;
32     LONG EqualChars, TotalChars;
33 
34     /* Build a partial string with the smaller substring */
35     PartialString.Length = EqualString->Length;
36     PartialString.MaximumLength = InputString->MaximumLength;
37     PartialString.Buffer = InputString->Buffer;
38 
39     /* Check how many characters that need comparing */
40     EqualChars = 0;
41     TotalChars = (InputString->Length - EqualString->Length) / sizeof(WCHAR);
42 
43     /* If the substring is bigger, just fail immediately */
44     if (TotalChars < 0) return FALSE;
45 
46     /* Keep checking each character */
47     while (!RtlEqualUnicodeString(EqualString, &PartialString, CaseInSensitive))
48     {
49         /* Continue checking until all the required characters are equal */
50         PartialString.Buffer++;
51         PartialString.MaximumLength -= sizeof(WCHAR);
52         if (++EqualChars > TotalChars) return FALSE;
53     }
54 
55     /* The string is equal */
56     return TRUE;
57 }
58 
59 BOOLEAN
60 NTAPI
61 PciStringToUSHORT(IN PWCHAR String,
62                   OUT PUSHORT Value)
63 {
64     USHORT Short;
65     ULONG Low, High, Length;
66     WCHAR Char;
67 
68     /* Initialize everything to zero */
69     Short = 0;
70     Length = 0;
71     while (TRUE)
72     {
73         /* Get the character and set the high byte based on the previous one */
74         Char = *String++;
75         High = 16 * Short;
76 
77         /* Check for numbers */
78         if ( Char >= '0' && Char <= '9' )
79         {
80             /* Convert them to a byte */
81             Low = Char - '0';
82         }
83         else if ( Char >= 'A' && Char <= 'F' )
84         {
85             /* Convert upper-case hex letters into a byte */
86             Low = Char - '7';
87         }
88         else if ( Char >= 'a' && Char <= 'f' )
89         {
90             /* Convert lower-case hex letters into a byte */
91             Low = Char - 'W';
92         }
93         else
94         {
95             /* Invalid string, fail the conversion */
96             return FALSE;
97         }
98 
99         /* Combine the high and low byte */
100         Short = High | Low;
101 
102         /* If 4 letters have been reached, the 16-bit integer should exist */
103         if (++Length >= 4)
104         {
105             /* Return it to the caller */
106             *Value = Short;
107             return TRUE;
108         }
109     }
110 }
111 
112 BOOLEAN
113 NTAPI
114 PciIsSuiteVersion(IN USHORT SuiteMask)
115 {
116     ULONGLONG Mask = 0;
117     RTL_OSVERSIONINFOEXW VersionInfo;
118 
119     /* Initialize the version information */
120     RtlZeroMemory(&VersionInfo, sizeof(RTL_OSVERSIONINFOEXW));
121     VersionInfo.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW);
122     VersionInfo.wSuiteMask = SuiteMask;
123 
124     /* Set the comparison mask and return if the passed suite mask matches */
125     VER_SET_CONDITION(Mask, VER_SUITENAME, VER_AND);
126     return NT_SUCCESS(RtlVerifyVersionInfo(&VersionInfo, VER_SUITENAME, Mask));
127 }
128 
129 BOOLEAN
130 NTAPI
131 PciIsDatacenter(VOID)
132 {
133     BOOLEAN Result;
134     PVOID Value;
135     ULONG ResultLength;
136     NTSTATUS Status;
137 
138     /* Assume this isn't Datacenter */
139     Result = FALSE;
140 
141     /* First, try opening the setup key */
142     Status = PciGetRegistryValue(L"",
143                                  L"\\REGISTRY\\MACHINE\\SYSTEM\\CurrentControlSet\\Services\\setupdd",
144                                  0,
145                                  REG_BINARY,
146                                  &Value,
147                                  &ResultLength);
148     if (!NT_SUCCESS(Status))
149     {
150         /* This is not an in-progress Setup boot, so query the suite version */
151         Result = PciIsSuiteVersion(VER_SUITE_DATACENTER);
152     }
153     else
154     {
155         /* This scenario shouldn't happen yet, since SetupDD isn't used */
156         UNIMPLEMENTED_FATAL("ReactOS doesn't use SetupDD for its installation program. Therefore this scenario must not happen!\n");
157     }
158 
159     /* Return if this is Datacenter or not */
160     return Result;
161 }
162 
163 BOOLEAN
164 NTAPI
165 PciOpenKey(IN PWCHAR KeyName,
166            IN HANDLE RootKey,
167            IN ACCESS_MASK DesiredAccess,
168            OUT PHANDLE KeyHandle,
169            OUT PNTSTATUS KeyStatus)
170 {
171     NTSTATUS Status;
172     OBJECT_ATTRIBUTES ObjectAttributes;
173     UNICODE_STRING KeyString;
174     PAGED_CODE();
175 
176     /* Initialize the object attributes */
177     RtlInitUnicodeString(&KeyString, KeyName);
178     InitializeObjectAttributes(&ObjectAttributes,
179                                &KeyString,
180                                OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
181                                RootKey,
182                                NULL);
183 
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);
188 }
189 
190 NTSTATUS
191 NTAPI
192 PciGetRegistryValue(IN PWCHAR ValueName,
193                     IN PWCHAR KeyName,
194                     IN HANDLE RootHandle,
195                     IN ULONG Type,
196                     OUT PVOID *OutputBuffer,
197                     OUT PULONG OutputLength)
198 {
199     NTSTATUS Status;
200     PKEY_VALUE_PARTIAL_INFORMATION PartialInfo;
201     ULONG NeededLength, ActualLength;
202     UNICODE_STRING ValueString;
203     HANDLE KeyHandle;
204     BOOLEAN Result;
205 
206     /* So we know what to free at the end of the body */
207     PartialInfo = NULL;
208     KeyHandle = NULL;
209     do
210     {
211         /* Open the key by name, rooted off the handle passed */
212         Result = PciOpenKey(KeyName,
213                             RootHandle,
214                             KEY_QUERY_VALUE,
215                             &KeyHandle,
216                             &Status);
217         if (!Result) break;
218 
219         /* Query for the size that's needed for the value that was passed in */
220         RtlInitUnicodeString(&ValueString, ValueName);
221         Status = ZwQueryValueKey(KeyHandle,
222                                  &ValueString,
223                                  KeyValuePartialInformation,
224                                  NULL,
225                                  0,
226                                  &NeededLength);
227         ASSERT(!NT_SUCCESS(Status));
228         if (Status != STATUS_BUFFER_TOO_SMALL) break;
229 
230         /* Allocate an appropriate buffer for the size that was returned */
231         ASSERT(NeededLength != 0);
232         Status = STATUS_INSUFFICIENT_RESOURCES;
233         PartialInfo = ExAllocatePoolWithTag(PagedPool,
234                                             NeededLength,
235                                             PCI_POOL_TAG);
236         if (!PartialInfo) break;
237 
238         /* Query the actual value information now that the size is known */
239         Status = ZwQueryValueKey(KeyHandle,
240                                  &ValueString,
241                                  KeyValuePartialInformation,
242                                  PartialInfo,
243                                  NeededLength,
244                                  &ActualLength);
245         if (!NT_SUCCESS(Status)) break;
246 
247         /* Make sure it's of the type that the caller expects */
248         Status = STATUS_INVALID_PARAMETER;
249         if (PartialInfo->Type != Type) break;
250 
251         /* Subtract the registry-specific header, to get the data size */
252         ASSERT(NeededLength == ActualLength);
253         NeededLength -= sizeof(KEY_VALUE_PARTIAL_INFORMATION);
254 
255         /* Allocate a buffer to hold the data and return it to the caller */
256         Status = STATUS_INSUFFICIENT_RESOURCES;
257         *OutputBuffer = ExAllocatePoolWithTag(PagedPool,
258                                               NeededLength,
259                                               PCI_POOL_TAG);
260         if (!*OutputBuffer) break;
261 
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;
266     } while (0);
267 
268     /* Close any opened keys and free temporary allocations */
269     if (KeyHandle) ZwClose(KeyHandle);
270     if (PartialInfo) ExFreePoolWithTag(PartialInfo, 0);
271     return Status;
272 }
273 
274 NTSTATUS
275 NTAPI
276 PciBuildDefaultExclusionLists(VOID)
277 {
278     ULONG Start;
279     NTSTATUS Status;
280     ASSERT(PciIsaBitExclusionList.Count == 0);
281     ASSERT(PciVgaAndIsaBitExclusionList.Count == 0);
282 
283     /* Initialize the range lists */
284     RtlInitializeRangeList(&PciIsaBitExclusionList);
285     RtlInitializeRangeList(&PciVgaAndIsaBitExclusionList);
286 
287     /* Loop x86 I/O ranges */
288     for (Start = 0x100; Start <= 0xFEFF; Start += 0x400)
289     {
290         /* Add the ISA I/O ranges */
291         Status = RtlAddRange(&PciIsaBitExclusionList,
292                              Start,
293                              Start + 0x2FF,
294                              0,
295                              RTL_RANGE_LIST_ADD_IF_CONFLICT,
296                              NULL,
297                              NULL);
298         if (!NT_SUCCESS(Status)) break;
299 
300         /* Add the ISA I/O ranges */
301         Status = RtlAddRange(&PciVgaAndIsaBitExclusionList,
302                              Start,
303                              Start + 0x2AF,
304                              0,
305                              RTL_RANGE_LIST_ADD_IF_CONFLICT,
306                              NULL,
307                              NULL);
308         if (!NT_SUCCESS(Status)) break;
309 
310         /* Add the VGA I/O range for Monochrome Video */
311         Status = RtlAddRange(&PciVgaAndIsaBitExclusionList,
312                              Start + 0x2BC,
313                              Start + 0x2BF,
314                              0,
315                              RTL_RANGE_LIST_ADD_IF_CONFLICT,
316                              NULL,
317                              NULL);
318         if (!NT_SUCCESS(Status)) break;
319 
320         /* Add the VGA I/O range for certain CGA adapters */
321         Status = RtlAddRange(&PciVgaAndIsaBitExclusionList,
322                              Start + 0x2E0,
323                              Start + 0x2FF,
324                              0,
325                              RTL_RANGE_LIST_ADD_IF_CONFLICT,
326                              NULL,
327                              NULL);
328         if (!NT_SUCCESS(Status)) break;
329 
330         /* Success, ranges added done */
331     };
332 
333     RtlFreeRangeList(&PciIsaBitExclusionList);
334     RtlFreeRangeList(&PciVgaAndIsaBitExclusionList);
335     return Status;
336 }
337 
338 PPCI_FDO_EXTENSION
339 NTAPI
340 PciFindParentPciFdoExtension(IN PDEVICE_OBJECT DeviceObject,
341                              IN PKEVENT Lock)
342 {
343     PPCI_FDO_EXTENSION DeviceExtension;
344     PPCI_PDO_EXTENSION SearchExtension, FoundExtension;
345 
346     /* Assume we'll find nothing */
347     SearchExtension = DeviceObject->DeviceExtension;
348     FoundExtension = NULL;
349 
350     /* Check if a lock was specified */
351     if (Lock)
352     {
353         /* Wait for the lock to be released */
354         KeEnterCriticalRegion();
355         KeWaitForSingleObject(Lock, Executive, KernelMode, FALSE, NULL);
356     }
357 
358     /* Now search for the extension */
359     DeviceExtension = (PPCI_FDO_EXTENSION)PciFdoExtensionListHead.Next;
360     while (DeviceExtension)
361     {
362         /* Acquire this device's lock */
363         KeEnterCriticalRegion();
364         KeWaitForSingleObject(&DeviceExtension->ChildListLock,
365                               Executive,
366                               KernelMode,
367                               FALSE,
368                               NULL);
369 
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);
374 
375         /* Release this device's lock */
376         KeSetEvent(&DeviceExtension->ChildListLock, IO_NO_INCREMENT, FALSE);
377         KeLeaveCriticalRegion();
378 
379         /* If we found it, break out */
380         if (FoundExtension) break;
381 
382         /* Move to the next device */
383         DeviceExtension = (PPCI_FDO_EXTENSION)DeviceExtension->List.Next;
384     }
385 
386     /* Check if we had acquired a lock previously */
387     if (Lock)
388     {
389         /* Release it */
390         KeSetEvent(Lock, IO_NO_INCREMENT, FALSE);
391         KeLeaveCriticalRegion();
392     }
393 
394     /* Return which extension was found, if any */
395     return DeviceExtension;
396 }
397 
398 VOID
399 NTAPI
400 PciInsertEntryAtTail(IN PSINGLE_LIST_ENTRY ListHead,
401                      IN PPCI_FDO_EXTENSION DeviceExtension,
402                      IN PKEVENT Lock)
403 {
404     PSINGLE_LIST_ENTRY NextEntry;
405     PAGED_CODE();
406 
407     /* Check if a lock was specified */
408     if (Lock)
409     {
410         /* Wait for the lock to be released */
411         KeEnterCriticalRegion();
412         KeWaitForSingleObject(Lock, Executive, KernelMode, FALSE, NULL);
413     }
414 
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;
418 
419     /* Check if we had acquired a lock previously */
420     if (Lock)
421     {
422         /* Release it */
423         KeSetEvent(Lock, IO_NO_INCREMENT, FALSE);
424         KeLeaveCriticalRegion();
425     }
426 }
427 
428 VOID
429 NTAPI
430 PciInsertEntryAtHead(IN PSINGLE_LIST_ENTRY ListHead,
431                      IN PSINGLE_LIST_ENTRY Entry,
432                      IN PKEVENT Lock)
433 {
434     PAGED_CODE();
435 
436     /* Check if a lock was specified */
437     if (Lock)
438     {
439         /* Wait for the lock to be released */
440         KeEnterCriticalRegion();
441         KeWaitForSingleObject(Lock, Executive, KernelMode, FALSE, NULL);
442     }
443 
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;
447 
448     /* Check if we had acquired a lock previously */
449     if (Lock)
450     {
451         /* Release it */
452         KeSetEvent(Lock, IO_NO_INCREMENT, FALSE);
453         KeLeaveCriticalRegion();
454     }
455 }
456 
457 VOID
458 NTAPI
459 PcipLinkSecondaryExtension(IN PSINGLE_LIST_ENTRY List,
460                            IN PVOID Lock,
461                            IN PPCI_SECONDARY_EXTENSION SecondaryExtension,
462                            IN PCI_SIGNATURE ExtensionType,
463                            IN PVOID Destructor)
464 {
465     PAGED_CODE();
466 
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);
471 }
472 
473 NTSTATUS
474 NTAPI
475 PciGetDeviceProperty(IN PDEVICE_OBJECT DeviceObject,
476                      IN DEVICE_REGISTRY_PROPERTY DeviceProperty,
477                      OUT PVOID *OutputBuffer)
478 {
479     NTSTATUS Status;
480     ULONG BufferLength, ResultLength;
481     PVOID Buffer;
482     do
483     {
484         /* Query the requested property size */
485         Status = IoGetDeviceProperty(DeviceObject,
486                                      DeviceProperty,
487                                      0,
488                                      NULL,
489                                      &BufferLength);
490         if (Status != STATUS_BUFFER_TOO_SMALL)
491         {
492             /* Call should've failed with buffer too small! */
493             DPRINT1("PCI - Unexpected status from GetDeviceProperty, saw %08X, expected %08X.\n",
494                     Status,
495                     STATUS_BUFFER_TOO_SMALL);
496             *OutputBuffer = NULL;
497             ASSERTMSG("PCI Successfully did the impossible!\n", FALSE);
498             break;
499         }
500 
501         /* Allocate the required buffer */
502         Buffer = ExAllocatePoolWithTag(PagedPool, BufferLength, 'BicP');
503         if (!Buffer)
504         {
505             /* No memory, fail the request */
506             DPRINT1("PCI - Failed to allocate DeviceProperty buffer (%u bytes).\n", BufferLength);
507             Status = STATUS_INSUFFICIENT_RESOURCES;
508             break;
509         }
510 
511         /* Do the actual property query call */
512         Status = IoGetDeviceProperty(DeviceObject,
513                                      DeviceProperty,
514                                      BufferLength,
515                                      Buffer,
516                                      &ResultLength);
517         if (!NT_SUCCESS(Status)) break;
518 
519         /* Return the buffer to the caller */
520         ASSERT(BufferLength == ResultLength);
521         *OutputBuffer = Buffer;
522         return STATUS_SUCCESS;
523     } while (FALSE);
524 
525     /* Failure path */
526     return STATUS_UNSUCCESSFUL;
527 }
528 
529 NTSTATUS
530 NTAPI
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)
537 {
538     PIRP Irp;
539     NTSTATUS Status;
540     KEVENT Event;
541     IO_STATUS_BLOCK IoStatusBlock;
542     PDEVICE_OBJECT AttachedDevice;
543     PAGED_CODE();
544 
545     /* Initialize the pending IRP event */
546     KeInitializeEvent(&Event, SynchronizationEvent, FALSE);
547 
548     /* Get a reference to the root PDO (ACPI) */
549     AttachedDevice = IoGetAttachedDeviceReference(DeviceObject);
550     if (!AttachedDevice) return STATUS_INVALID_PARAMETER;
551 
552     /* Build the requested IOCTL IRP */
553     Irp = IoBuildDeviceIoControlRequest(IoControlCode,
554                                         AttachedDevice,
555                                         InputBuffer,
556                                         InputBufferLength,
557                                         OutputBuffer,
558                                         OutputBufferLength,
559                                         0,
560                                         &Event,
561                                         &IoStatusBlock);
562     if (!Irp) return STATUS_INSUFFICIENT_RESOURCES;
563 
564     /* Send the IOCTL to the driver */
565     Status = IoCallDriver(AttachedDevice, Irp);
566     if (Status == STATUS_PENDING)
567     {
568         /* Wait for a response */
569         KeWaitForSingleObject(&Event,
570                               Executive,
571                               KernelMode,
572                               FALSE,
573                               NULL);
574         Status = Irp->IoStatus.Status;
575     }
576 
577     /* Take away the reference we took and return the result to the caller */
578     ObDereferenceObject(AttachedDevice);
579     return Status;
580 }
581 
582 PPCI_SECONDARY_EXTENSION
583 NTAPI
584 PciFindNextSecondaryExtension(IN PSINGLE_LIST_ENTRY ListHead,
585                               IN PCI_SIGNATURE ExtensionType)
586 {
587     PSINGLE_LIST_ENTRY NextEntry;
588     PPCI_SECONDARY_EXTENSION Extension;
589 
590     /* Scan the list */
591     for (NextEntry = ListHead; NextEntry; NextEntry = NextEntry->Next)
592     {
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;
596     }
597 
598     /* Nothing was found */
599     return NULL;
600 }
601 
602 ULONGLONG
603 NTAPI
604 PciGetHackFlags(IN USHORT VendorId,
605                 IN USHORT DeviceId,
606                 IN USHORT SubVendorId,
607                 IN USHORT SubSystemId,
608                 IN UCHAR RevisionId)
609 {
610     PPCI_HACK_ENTRY HackEntry;
611     ULONGLONG HackFlags;
612     ULONG LastWeight, MatchWeight;
613     ULONG EntryFlags;
614 
615     /* ReactOS SetupLDR Hack */
616     if (!PciHackTable) return 0;
617 
618     /* Initialize the variables before looping */
619     LastWeight = 0;
620     HackFlags = 0;
621     ASSERT(PciHackTable);
622 
623     /* Scan the hack table */
624     for (HackEntry = PciHackTable;
625          HackEntry->VendorID != PCI_INVALID_VENDORID;
626          ++HackEntry)
627     {
628         /* Check if there's an entry for this device */
629         if ((HackEntry->DeviceID == DeviceId) &&
630             (HackEntry->VendorID == VendorId))
631         {
632             /* This is a basic match */
633             EntryFlags = HackEntry->Flags;
634             MatchWeight = 1;
635 
636             /* Does the entry have revision information? */
637             if (EntryFlags & PCI_HACK_HAS_REVISION_INFO)
638             {
639                 /* Check if the revision matches, if so, this is a better match */
640                 if (HackEntry->RevisionID != RevisionId) continue;
641                 MatchWeight = 3;
642             }
643 
644             /* Does the netry have subsystem information? */
645             if (EntryFlags & PCI_HACK_HAS_SUBSYSTEM_INFO)
646             {
647                 /* Check if it matches, if so, this is the best possible match */
648                 if ((HackEntry->SubVendorID != SubVendorId) ||
649                     (HackEntry->SubSystemID != SubSystemId))
650                 {
651                     continue;
652                 }
653                 MatchWeight += 4;
654             }
655 
656             /* Is this the best match yet? */
657             if (MatchWeight > LastWeight)
658             {
659                 /* This is the best match for now, use this as the hack flags */
660                 HackFlags = HackEntry->HackFlags;
661                 LastWeight = MatchWeight;
662             }
663         }
664     }
665 
666     /* Return the best match */
667     return HackFlags;
668 }
669 
670 BOOLEAN
671 NTAPI
672 PciIsCriticalDeviceClass(IN UCHAR BaseClass,
673                          IN UCHAR SubClass)
674 {
675     /* Check for system or bridge devices */
676     if (BaseClass == PCI_CLASS_BASE_SYSTEM_DEV)
677     {
678         /* Interrupt controllers are critical */
679         return SubClass == PCI_SUBCLASS_SYS_INTERRUPT_CTLR;
680     }
681     else if (BaseClass == PCI_CLASS_BRIDGE_DEV)
682     {
683         /* ISA Bridges are critical */
684         return SubClass == PCI_SUBCLASS_BR_ISA;
685     }
686     else
687     {
688         /* All display controllers are critical */
689         return BaseClass == PCI_CLASS_DISPLAY_CTLR;
690     }
691 }
692 
693 PPCI_PDO_EXTENSION
694 NTAPI
695 PciFindPdoByFunction(IN PPCI_FDO_EXTENSION DeviceExtension,
696                      IN ULONG FunctionNumber,
697                      IN PPCI_COMMON_HEADER PciData)
698 {
699     KIRQL Irql;
700     PPCI_PDO_EXTENSION PdoExtension;
701 
702     /* Get the current IRQL when this call was made */
703     Irql = KeGetCurrentIrql();
704 
705     /* Is this a low-IRQL call? */
706     if (Irql < DISPATCH_LEVEL)
707     {
708         /* Acquire this device's lock */
709         KeEnterCriticalRegion();
710         KeWaitForSingleObject(&DeviceExtension->ChildListLock,
711                               Executive,
712                               KernelMode,
713                               FALSE,
714                               NULL);
715     }
716 
717     /* Loop every child PDO */
718     for (PdoExtension = DeviceExtension->ChildPdoList;
719          PdoExtension;
720          PdoExtension = PdoExtension->Next)
721     {
722         /* Find only enumerated PDOs */
723         if (!PdoExtension->ReportedMissing)
724         {
725             /* Check if the function number and header data matches */
726             if ((FunctionNumber == PdoExtension->Slot.u.AsULONG) &&
727                 (PdoExtension->VendorId == PciData->VendorID) &&
728                 (PdoExtension->DeviceId == PciData->DeviceID) &&
729                 (PdoExtension->RevisionId == PciData->RevisionID))
730             {
731                 /* This is considered to be the same PDO */
732                 break;
733             }
734         }
735     }
736 
737     /* Was this a low-IRQL call? */
738     if (Irql < DISPATCH_LEVEL)
739     {
740         /* Release this device's lock */
741         KeSetEvent(&DeviceExtension->ChildListLock, IO_NO_INCREMENT, FALSE);
742         KeLeaveCriticalRegion();
743     }
744 
745     /* If the search found something, this is non-NULL, otherwise it's NULL */
746     return PdoExtension;
747 }
748 
749 BOOLEAN
750 NTAPI
751 PciIsDeviceOnDebugPath(IN PPCI_PDO_EXTENSION DeviceExtension)
752 {
753     PAGED_CODE();
754 
755     UNREFERENCED_PARAMETER(DeviceExtension);
756 
757     /* Check for too many, or no, debug ports */
758     ASSERT(PciDebugPortsCount <= MAX_DEBUGGING_DEVICES_SUPPORTED);
759     if (!PciDebugPortsCount) return FALSE;
760 
761     /* eVb has not been able to test such devices yet */
762     UNIMPLEMENTED_DBGBREAK();
763     return FALSE;
764 }
765 
766 NTSTATUS
767 NTAPI
768 PciGetBiosConfig(IN PPCI_PDO_EXTENSION DeviceExtension,
769                  OUT PPCI_COMMON_HEADER PciData)
770 {
771     HANDLE KeyHandle, SubKeyHandle;
772     OBJECT_ATTRIBUTES ObjectAttributes;
773     UNICODE_STRING KeyName, KeyValue;
774     WCHAR Buffer[32];
775     WCHAR DataBuffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + PCI_COMMON_HDR_LENGTH];
776     PKEY_VALUE_PARTIAL_INFORMATION PartialInfo = (PVOID)DataBuffer;
777     NTSTATUS Status;
778     ULONG ResultLength;
779     PAGED_CODE();
780 
781     /* Open the PCI key */
782     Status = IoOpenDeviceRegistryKey(DeviceExtension->ParentFdoExtension->
783                                      PhysicalDeviceObject,
784                                      TRUE,
785                                      KEY_ALL_ACCESS,
786                                      &KeyHandle);
787     if (!NT_SUCCESS(Status)) return Status;
788 
789     /* Create a volatile BIOS configuration key */
790     RtlInitUnicodeString(&KeyName, L"BiosConfig");
791     InitializeObjectAttributes(&ObjectAttributes,
792                                &KeyName,
793                                OBJ_KERNEL_HANDLE,
794                                KeyHandle,
795                                NULL);
796     Status = ZwCreateKey(&SubKeyHandle,
797                          KEY_READ,
798                          &ObjectAttributes,
799                          0,
800                          NULL,
801                          REG_OPTION_VOLATILE,
802                          NULL);
803     ZwClose(KeyHandle);
804     if (!NT_SUCCESS(Status)) return Status;
805 
806     /* Create the key value based on the device and function number */
807     swprintf(Buffer,
808              L"DEV_%02x&FUN_%02x",
809              DeviceExtension->Slot.u.bits.DeviceNumber,
810              DeviceExtension->Slot.u.bits.FunctionNumber);
811     RtlInitUnicodeString(&KeyValue, Buffer);
812 
813     /* Query the value information (PCI BIOS configuration header) */
814     Status = ZwQueryValueKey(SubKeyHandle,
815                              &KeyValue,
816                              KeyValuePartialInformation,
817                              PartialInfo,
818                              sizeof(DataBuffer),
819                              &ResultLength);
820     ZwClose(SubKeyHandle);
821     if (!NT_SUCCESS(Status)) return Status;
822 
823     /* If any information was returned, go ahead and copy its data */
824     ASSERT(PartialInfo->DataLength == PCI_COMMON_HDR_LENGTH);
825     RtlCopyMemory(PciData, PartialInfo->Data, PCI_COMMON_HDR_LENGTH);
826     return Status;
827 }
828 
829 NTSTATUS
830 NTAPI
831 PciSaveBiosConfig(IN PPCI_PDO_EXTENSION DeviceExtension,
832                   IN PPCI_COMMON_HEADER PciData)
833 {
834     HANDLE KeyHandle, SubKeyHandle;
835     OBJECT_ATTRIBUTES ObjectAttributes;
836     UNICODE_STRING KeyName, KeyValue;
837     WCHAR Buffer[32];
838     NTSTATUS Status;
839     PAGED_CODE();
840 
841     /* Open the PCI key */
842     Status = IoOpenDeviceRegistryKey(DeviceExtension->ParentFdoExtension->
843                                      PhysicalDeviceObject,
844                                      TRUE,
845                                      KEY_READ | KEY_WRITE,
846                                      &KeyHandle);
847     if (!NT_SUCCESS(Status)) return Status;
848 
849     /* Create a volatile BIOS configuration key */
850     RtlInitUnicodeString(&KeyName, L"BiosConfig");
851     InitializeObjectAttributes(&ObjectAttributes,
852                                &KeyName,
853                                OBJ_KERNEL_HANDLE,
854                                KeyHandle,
855                                NULL);
856     Status = ZwCreateKey(&SubKeyHandle,
857                          KEY_READ | KEY_WRITE,
858                          &ObjectAttributes,
859                          0,
860                          NULL,
861                          REG_OPTION_VOLATILE,
862                          NULL);
863     ZwClose(KeyHandle);
864     if (!NT_SUCCESS(Status)) return Status;
865 
866     /* Create the key value based on the device and function number */
867     swprintf(Buffer,
868              L"DEV_%02x&FUN_%02x",
869              DeviceExtension->Slot.u.bits.DeviceNumber,
870              DeviceExtension->Slot.u.bits.FunctionNumber);
871     RtlInitUnicodeString(&KeyValue, Buffer);
872 
873     /* Set the value data (the PCI BIOS configuration header) */
874     Status = ZwSetValueKey(SubKeyHandle,
875                            &KeyValue,
876                            0,
877                            REG_BINARY,
878                            PciData,
879                            PCI_COMMON_HDR_LENGTH);
880     ZwClose(SubKeyHandle);
881     return Status;
882 }
883 
884 UCHAR
885 NTAPI
886 PciReadDeviceCapability(IN PPCI_PDO_EXTENSION DeviceExtension,
887                         IN UCHAR Offset,
888                         IN ULONG CapabilityId,
889                         OUT PPCI_CAPABILITIES_HEADER Buffer,
890                         IN ULONG Length)
891 {
892     ULONG CapabilityCount = 0;
893 
894     /* If the device has no capabilility list, fail */
895     if (!Offset) return 0;
896 
897     /* Validate a PDO with capabilities, a valid buffer, and a valid length */
898     ASSERT(DeviceExtension->ExtensionType == PciPdoExtensionType);
899     ASSERT(DeviceExtension->CapabilitiesPtr != 0);
900     ASSERT(Buffer);
901     ASSERT(Length >= sizeof(PCI_CAPABILITIES_HEADER));
902 
903     /* Loop all capabilities */
904     while (Offset)
905     {
906         /* Make sure the pointer is spec-aligned and spec-sized */
907         ASSERT((Offset >= PCI_COMMON_HDR_LENGTH) && ((Offset & 0x3) == 0));
908 
909         /* Read the capability header */
910         PciReadDeviceConfig(DeviceExtension,
911                             Buffer,
912                             Offset,
913                             sizeof(PCI_CAPABILITIES_HEADER));
914 
915         /* Check if this is the capability being looked up */
916         if ((Buffer->CapabilityID == CapabilityId) || !(CapabilityId))
917         {
918             /* Check if was at a valid offset and length */
919             if ((Offset) && (Length > sizeof(PCI_CAPABILITIES_HEADER)))
920             {
921                 /* Sanity check */
922                 ASSERT(Length <= (sizeof(PCI_COMMON_CONFIG) - Offset));
923 
924                 /* Now read the whole capability data into the buffer */
925                 PciReadDeviceConfig(DeviceExtension,
926                                     (PVOID)((ULONG_PTR)Buffer +
927                                     sizeof(PCI_CAPABILITIES_HEADER)),
928                                     Offset + sizeof(PCI_CAPABILITIES_HEADER),
929                                     Length - sizeof(PCI_CAPABILITIES_HEADER));
930             }
931 
932             /* Return the offset where the capability was found */
933             return Offset;
934         }
935 
936         /* Try the next capability instead */
937         CapabilityCount++;
938         Offset = Buffer->Next;
939 
940         /* There can't be more than 48 capabilities (256 bytes max) */
941         if (CapabilityCount > 48)
942         {
943             /* Fail, since this is basically a broken PCI device */
944             DPRINT1("PCI device %p capabilities list is broken.\n", DeviceExtension);
945             return 0;
946         }
947     }
948 
949     /* Capability wasn't found, fail */
950     return 0;
951 }
952 
953 BOOLEAN
954 NTAPI
955 PciCanDisableDecodes(IN PPCI_PDO_EXTENSION DeviceExtension,
956                      IN PPCI_COMMON_HEADER Config,
957                      IN ULONGLONG HackFlags,
958                      IN BOOLEAN ForPowerDown)
959 {
960     UCHAR BaseClass, SubClass;
961     BOOLEAN IsVga;
962 
963     /* Is there a device extension or should the PCI header be used? */
964     if (DeviceExtension)
965     {
966         /* Never disable decodes for a debug PCI Device */
967         if (DeviceExtension->OnDebugPath) return FALSE;
968 
969         /* Hack flags will be obtained from the extension, not the caller */
970         ASSERT(HackFlags == 0);
971 
972         /* Get hacks and classification from the device extension */
973         HackFlags = DeviceExtension->HackFlags;
974         SubClass = DeviceExtension->SubClass;
975         BaseClass = DeviceExtension->BaseClass;
976     }
977     else
978     {
979         /* There must be a PCI header, go read the classification information */
980         ASSERT(Config != NULL);
981         BaseClass = Config->BaseClass;
982         SubClass = Config->SubClass;
983     }
984 
985     /* Check for hack flags that prevent disabling the decodes */
986     if (HackFlags & (PCI_HACK_PRESERVE_COMMAND |
987                      PCI_HACK_CB_SHARE_CMD_BITS |
988                      PCI_HACK_DONT_DISABLE_DECODES))
989     {
990         /* Don't do it */
991         return FALSE;
992     }
993 
994     /* Is this a VGA adapter? */
995     if ((BaseClass == PCI_CLASS_DISPLAY_CTLR) &&
996         (SubClass == PCI_SUBCLASS_VID_VGA_CTLR))
997     {
998         /* Never disable decodes if this is for power down */
999         return ForPowerDown;
1000     }
1001 
1002     /* Check for legacy devices */
1003     if (BaseClass == PCI_CLASS_PRE_20)
1004     {
1005         /* Never disable video adapter cards if this is for power down */
1006         if (SubClass == PCI_SUBCLASS_PRE_20_VGA) return ForPowerDown;
1007     }
1008     else if (BaseClass == PCI_CLASS_DISPLAY_CTLR)
1009     {
1010         /* Never disable VGA adapters if this is for power down */
1011         if (SubClass == PCI_SUBCLASS_VID_VGA_CTLR) return ForPowerDown;
1012     }
1013     else if (BaseClass == PCI_CLASS_BRIDGE_DEV)
1014     {
1015         /* Check for legacy bridges */
1016         if ((SubClass == PCI_SUBCLASS_BR_ISA) ||
1017             (SubClass == PCI_SUBCLASS_BR_EISA) ||
1018             (SubClass == PCI_SUBCLASS_BR_MCA) ||
1019             (SubClass == PCI_SUBCLASS_BR_HOST) ||
1020             (SubClass == PCI_SUBCLASS_BR_OTHER))
1021         {
1022             /* Never disable these */
1023             return FALSE;
1024         }
1025         else if ((SubClass == PCI_SUBCLASS_BR_PCI_TO_PCI) ||
1026                  (SubClass == PCI_SUBCLASS_BR_CARDBUS))
1027         {
1028             /* This is a supported bridge, but does it have a VGA card? */
1029             if (!DeviceExtension)
1030             {
1031                 /* Read the bridge control flag from the PCI header */
1032                 IsVga = Config->u.type1.BridgeControl & PCI_ENABLE_BRIDGE_VGA;
1033             }
1034             else
1035             {
1036                 /* Read the cached flag in the device extension */
1037                 IsVga = DeviceExtension->Dependent.type1.VgaBitSet;
1038             }
1039 
1040             /* Never disable VGA adapters if this is for power down */
1041             if (IsVga) return ForPowerDown;
1042         }
1043     }
1044 
1045     /* Finally, never disable decodes if there's no power management */
1046     return !(HackFlags & PCI_HACK_NO_PM_CAPS);
1047 }
1048 
1049 PCI_DEVICE_TYPES
1050 NTAPI
1051 PciClassifyDeviceType(IN PPCI_PDO_EXTENSION PdoExtension)
1052 {
1053     ASSERT(PdoExtension->ExtensionType == PciPdoExtensionType);
1054 
1055     /* Differentiate between devices and bridges */
1056     if (PdoExtension->BaseClass != PCI_CLASS_BRIDGE_DEV) return PciTypeDevice;
1057 
1058     /* The PCI Bus driver handles only CardBus and PCI bridges (plus host) */
1059     if (PdoExtension->SubClass == PCI_SUBCLASS_BR_HOST) return PciTypeHostBridge;
1060     if (PdoExtension->SubClass == PCI_SUBCLASS_BR_PCI_TO_PCI) return PciTypePciBridge;
1061     if (PdoExtension->SubClass == PCI_SUBCLASS_BR_CARDBUS) return PciTypeCardbusBridge;
1062 
1063     /* Any other kind of bridge is treated like a device */
1064     return PciTypeDevice;
1065 }
1066 
1067 ULONG_PTR
1068 NTAPI
1069 PciExecuteCriticalSystemRoutine(IN ULONG_PTR IpiContext)
1070 {
1071     PPCI_IPI_CONTEXT Context = (PPCI_IPI_CONTEXT)IpiContext;
1072 
1073     /* Check if the IPI is already running */
1074     if (!InterlockedDecrement(&Context->RunCount))
1075     {
1076         /* Nope, this is the first instance, so execute the IPI function */
1077         Context->Function(Context->DeviceExtension, Context->Context);
1078 
1079         /* Notify anyone that was spinning that they can stop now */
1080         Context->Barrier = 0;
1081     }
1082     else
1083     {
1084         /* Spin until it has finished running */
1085         while (Context->Barrier);
1086     }
1087 
1088     /* Done */
1089     return 0;
1090 }
1091 
1092 BOOLEAN
1093 NTAPI
1094 PciIsSlotPresentInParentMethod(IN PPCI_PDO_EXTENSION PdoExtension,
1095                                IN ULONG Method)
1096 {
1097     BOOLEAN FoundSlot;
1098     PACPI_METHOD_ARGUMENT Argument;
1099     ACPI_EVAL_INPUT_BUFFER InputBuffer;
1100     PACPI_EVAL_OUTPUT_BUFFER OutputBuffer;
1101     ULONG i, Length;
1102     NTSTATUS Status;
1103     PAGED_CODE();
1104 
1105     /* Assume slot is not part of the parent method */
1106     FoundSlot = FALSE;
1107 
1108     /* Allocate a 2KB buffer for the method return parameters */
1109     Length = sizeof(ACPI_EVAL_OUTPUT_BUFFER) + 2048;
1110     OutputBuffer = ExAllocatePoolWithTag(PagedPool, Length, 'BicP');
1111     if (OutputBuffer)
1112     {
1113         /* Clear out the output buffer */
1114         RtlZeroMemory(OutputBuffer, Length);
1115 
1116         /* Initialize the input buffer with the method requested */
1117         InputBuffer.Signature = 0;
1118         *(PULONG)InputBuffer.MethodName = Method;
1119         InputBuffer.Signature = ACPI_EVAL_INPUT_BUFFER_SIGNATURE;
1120 
1121         /* Send it to the ACPI driver */
1122         Status = PciSendIoctl(PdoExtension->ParentFdoExtension->PhysicalDeviceObject,
1123                               IOCTL_ACPI_EVAL_METHOD,
1124                               &InputBuffer,
1125                               sizeof(ACPI_EVAL_INPUT_BUFFER),
1126                               OutputBuffer,
1127                               Length);
1128         if (NT_SUCCESS(Status))
1129         {
1130             /* Scan all output arguments */
1131             for (i = 0; i < OutputBuffer->Count; i++)
1132             {
1133                 /* Make sure it's an integer */
1134                 Argument = &OutputBuffer->Argument[i];
1135                 if (Argument->Type != ACPI_METHOD_ARGUMENT_INTEGER) continue;
1136 
1137                 /* Check if the argument matches this PCI slot structure */
1138                 if (Argument->Argument == ((PdoExtension->Slot.u.bits.DeviceNumber) |
1139                                            ((PdoExtension->Slot.u.bits.FunctionNumber) << 16)))
1140                 {
1141                     /* This slot has been found, return it */
1142                     FoundSlot = TRUE;
1143                     break;
1144                 }
1145             }
1146         }
1147 
1148         /* Finished with the buffer, free it */
1149         ExFreePoolWithTag(OutputBuffer, 0);
1150     }
1151 
1152     /* Return if the slot was found */
1153     return FoundSlot;
1154 }
1155 
1156 ULONG
1157 NTAPI
1158 PciGetLengthFromBar(IN ULONG Bar)
1159 {
1160     ULONG Length;
1161 
1162     /* I/O addresses vs. memory addresses start differently due to alignment */
1163     Length = 1 << ((Bar & PCI_ADDRESS_IO_SPACE) ? 2 : 4);
1164 
1165     /* Keep going until a set bit */
1166     while (!(Length & Bar) && (Length)) Length <<= 1;
1167 
1168     /* Return the length (might be 0 on 64-bit because it's the low-word) */
1169     if ((Bar & PCI_ADDRESS_MEMORY_TYPE_MASK) != PCI_TYPE_64BIT) ASSERT(Length);
1170     return Length;
1171 }
1172 
1173 BOOLEAN
1174 NTAPI
1175 PciCreateIoDescriptorFromBarLimit(PIO_RESOURCE_DESCRIPTOR ResourceDescriptor,
1176                                   IN PULONG BarArray,
1177                                   IN BOOLEAN Rom)
1178 {
1179     ULONG CurrentBar, BarLength, BarMask;
1180     BOOLEAN Is64BitBar = FALSE;
1181 
1182     /* Check if the BAR is nor I/O nor memory */
1183     CurrentBar = BarArray[0];
1184     if (!(CurrentBar & ~PCI_ADDRESS_IO_SPACE))
1185     {
1186         /* Fail this descriptor */
1187         ResourceDescriptor->Type = CmResourceTypeNull;
1188         return FALSE;
1189     }
1190 
1191     /* Set default flag and clear high words */
1192     ResourceDescriptor->Flags = 0;
1193     ResourceDescriptor->u.Generic.MaximumAddress.HighPart = 0;
1194     ResourceDescriptor->u.Generic.MinimumAddress.LowPart = 0;
1195     ResourceDescriptor->u.Generic.MinimumAddress.HighPart = 0;
1196 
1197     /* Check for ROM Address */
1198     if (Rom)
1199     {
1200         /* Clean up the BAR to get just the address */
1201         CurrentBar &= PCI_ADDRESS_ROM_ADDRESS_MASK;
1202         if (!CurrentBar)
1203         {
1204             /* Invalid ar, fail this descriptor */
1205             ResourceDescriptor->Type = CmResourceTypeNull;
1206             return FALSE;
1207         }
1208 
1209         /* ROM Addresses are always read only */
1210         ResourceDescriptor->Flags = CM_RESOURCE_MEMORY_READ_ONLY;
1211     }
1212 
1213     /* Compute the length, assume it's the alignment for now */
1214     BarLength = PciGetLengthFromBar(CurrentBar);
1215     ResourceDescriptor->u.Generic.Length = BarLength;
1216     ResourceDescriptor->u.Generic.Alignment = BarLength;
1217 
1218     /* Check what kind of BAR this is */
1219     if (CurrentBar & PCI_ADDRESS_IO_SPACE)
1220     {
1221         /* Use correct mask to decode the address */
1222         BarMask = PCI_ADDRESS_IO_ADDRESS_MASK;
1223 
1224         /* Set this as an I/O Port descriptor */
1225         ResourceDescriptor->Type = CmResourceTypePort;
1226         ResourceDescriptor->Flags = CM_RESOURCE_PORT_IO;
1227     }
1228     else
1229     {
1230         /* Use correct mask to decode the address */
1231         BarMask = PCI_ADDRESS_MEMORY_ADDRESS_MASK;
1232 
1233         /* Set this as a memory descriptor */
1234         ResourceDescriptor->Type = CmResourceTypeMemory;
1235 
1236         /* Check if it's 64-bit or 20-bit decode */
1237         if ((CurrentBar & PCI_ADDRESS_MEMORY_TYPE_MASK) == PCI_TYPE_64BIT)
1238         {
1239             /* The next BAR has the high word, read it */
1240             ResourceDescriptor->u.Port.MaximumAddress.HighPart = BarArray[1];
1241             Is64BitBar = TRUE;
1242         }
1243         else if ((CurrentBar & PCI_ADDRESS_MEMORY_TYPE_MASK) == PCI_TYPE_20BIT)
1244         {
1245             /* Use the correct mask to decode the address */
1246             BarMask = ~0xFFF0000F;
1247         }
1248 
1249         /* Check if the BAR is listed as prefetchable memory */
1250         if (CurrentBar & PCI_ADDRESS_MEMORY_PREFETCHABLE)
1251         {
1252             /* Mark the descriptor in the same way */
1253             ResourceDescriptor->Flags |= CM_RESOURCE_MEMORY_PREFETCHABLE;
1254         }
1255     }
1256 
1257     /* Now write down the maximum address based on the base + length */
1258     ResourceDescriptor->u.Port.MaximumAddress.QuadPart = (CurrentBar & BarMask) +
1259                                                          BarLength - 1;
1260 
1261     /* Return if this is a 64-bit BAR, so the loop code knows to skip the next one */
1262     return Is64BitBar;
1263 }
1264 
1265 VOID
1266 NTAPI
1267 PciDecodeEnable(IN PPCI_PDO_EXTENSION PdoExtension,
1268                 IN BOOLEAN Enable,
1269                 OUT PUSHORT Command)
1270 {
1271     USHORT CommandValue;
1272 
1273     /*
1274      * If decodes are being disabled, make sure it's allowed, and in both cases,
1275      * make sure that a hackflag isn't preventing touching the decodes at all.
1276      */
1277     if (((Enable) || (PciCanDisableDecodes(PdoExtension, 0, 0, 0))) &&
1278         !(PdoExtension->HackFlags & PCI_HACK_PRESERVE_COMMAND))
1279     {
1280         /* Did the caller already have a command word? */
1281         if (Command)
1282         {
1283             /* Use the caller's */
1284             CommandValue = *Command;
1285         }
1286         else
1287         {
1288             /* Otherwise, read the current command */
1289             PciReadDeviceConfig(PdoExtension,
1290                                 &Command,
1291                                 FIELD_OFFSET(PCI_COMMON_HEADER, Command),
1292                                 sizeof(USHORT));
1293         }
1294 
1295         /* Turn off decodes by default */
1296         CommandValue &= ~(PCI_ENABLE_IO_SPACE |
1297                           PCI_ENABLE_MEMORY_SPACE |
1298                           PCI_ENABLE_BUS_MASTER);
1299 
1300         /* If requested, enable the decodes that were enabled at init time */
1301         if (Enable) CommandValue |= PdoExtension->CommandEnables &
1302                                     (PCI_ENABLE_IO_SPACE |
1303                                      PCI_ENABLE_MEMORY_SPACE |
1304                                      PCI_ENABLE_BUS_MASTER);
1305 
1306         /* Update the command word */
1307         PciWriteDeviceConfig(PdoExtension,
1308                              &CommandValue,
1309                              FIELD_OFFSET(PCI_COMMON_HEADER, Command),
1310                              sizeof(USHORT));
1311     }
1312 }
1313 
1314 NTSTATUS
1315 NTAPI
1316 PciQueryBusInformation(IN PPCI_PDO_EXTENSION PdoExtension,
1317                        IN PPNP_BUS_INFORMATION* Buffer)
1318 {
1319     PPNP_BUS_INFORMATION BusInfo;
1320 
1321     UNREFERENCED_PARAMETER(Buffer);
1322 
1323     /* Allocate a structure for the bus information */
1324     BusInfo = ExAllocatePoolWithTag(PagedPool,
1325                                     sizeof(PNP_BUS_INFORMATION),
1326                                     'BicP');
1327     if (!BusInfo) return STATUS_INSUFFICIENT_RESOURCES;
1328 
1329     /* Write the correct GUID and bus type identifier, and fill the bus number */
1330     BusInfo->BusTypeGuid = GUID_BUS_TYPE_PCI;
1331     BusInfo->LegacyBusType = PCIBus;
1332     BusInfo->BusNumber = PdoExtension->ParentFdoExtension->BaseBus;
1333     return STATUS_SUCCESS;
1334 }
1335 
1336 NTSTATUS
1337 NTAPI
1338 PciDetermineSlotNumber(IN PPCI_PDO_EXTENSION PdoExtension,
1339                        OUT PULONG SlotNumber)
1340 {
1341     PPCI_FDO_EXTENSION ParentExtension;
1342     ULONG ResultLength;
1343     NTSTATUS Status;
1344     PSLOT_INFO SlotInfo;
1345 
1346     /* Check if a $PIR from the BIOS is used (legacy IRQ routing) */
1347     ParentExtension = PdoExtension->ParentFdoExtension;
1348     DPRINT1("Slot lookup for %d.%u.%u\n",
1349             ParentExtension ? ParentExtension->BaseBus : -1,
1350             PdoExtension->Slot.u.bits.DeviceNumber,
1351             PdoExtension->Slot.u.bits.FunctionNumber);
1352     if ((PciIrqRoutingTable) && (ParentExtension))
1353     {
1354         /* Read every slot information entry */
1355         SlotInfo = &PciIrqRoutingTable->Slot[0];
1356         DPRINT1("PIR$ %p is %lx bytes, slot 0 is at: %p\n",
1357                 PciIrqRoutingTable, PciIrqRoutingTable->TableSize, SlotInfo);
1358         while (SlotInfo < (PSLOT_INFO)((ULONG_PTR)PciIrqRoutingTable +
1359                                        PciIrqRoutingTable->TableSize))
1360         {
1361             DPRINT1("Slot Info: %u.%u->#%u\n",
1362                     SlotInfo->BusNumber,
1363                     SlotInfo->DeviceNumber,
1364                     SlotInfo->SlotNumber);
1365 
1366             /* Check if this slot information matches the PDO being queried */
1367             if ((ParentExtension->BaseBus == SlotInfo->BusNumber) &&
1368                 (PdoExtension->Slot.u.bits.DeviceNumber == SlotInfo->DeviceNumber >> 3) &&
1369                 (SlotInfo->SlotNumber))
1370             {
1371                 /* We found it, return it and return success */
1372                 *SlotNumber = SlotInfo->SlotNumber;
1373                 return STATUS_SUCCESS;
1374             }
1375 
1376             /* Try the next slot */
1377             SlotInfo++;
1378         }
1379     }
1380 
1381     /* Otherwise, grab the parent FDO and check if it's the root */
1382     if (PCI_IS_ROOT_FDO(ParentExtension))
1383     {
1384         /* The root FDO doesn't have a slot number */
1385         Status = STATUS_UNSUCCESSFUL;
1386     }
1387     else
1388     {
1389         /* Otherwise, query the slot/UI address/number as a device property */
1390         Status = IoGetDeviceProperty(ParentExtension->PhysicalDeviceObject,
1391                                      DevicePropertyUINumber,
1392                                      sizeof(ULONG),
1393                                      SlotNumber,
1394                                      &ResultLength);
1395     }
1396 
1397     /* Return the status of this endeavour */
1398     return Status;
1399 }
1400 
1401 NTSTATUS
1402 NTAPI
1403 PciGetDeviceCapabilities(IN PDEVICE_OBJECT DeviceObject,
1404                          IN OUT PDEVICE_CAPABILITIES DeviceCapability)
1405 {
1406     PIRP Irp;
1407     NTSTATUS Status;
1408     KEVENT Event;
1409     PDEVICE_OBJECT AttachedDevice;
1410     PIO_STACK_LOCATION IoStackLocation;
1411     IO_STATUS_BLOCK IoStatusBlock;
1412     PAGED_CODE();
1413 
1414     /* Zero out capabilities and set undefined values to start with */
1415     RtlZeroMemory(DeviceCapability, sizeof(DEVICE_CAPABILITIES));
1416     DeviceCapability->Size = sizeof(DEVICE_CAPABILITIES);
1417     DeviceCapability->Version = 1;
1418     DeviceCapability->Address = -1;
1419     DeviceCapability->UINumber = -1;
1420 
1421     /* Build the wait event for the IOCTL */
1422     KeInitializeEvent(&Event, SynchronizationEvent, FALSE);
1423 
1424     /* Find the device the PDO is attached to */
1425     AttachedDevice = IoGetAttachedDeviceReference(DeviceObject);
1426 
1427     /* And build an IRP for it */
1428     Irp = IoBuildSynchronousFsdRequest(IRP_MJ_PNP,
1429                                        AttachedDevice,
1430                                        NULL,
1431                                        0,
1432                                        NULL,
1433                                        &Event,
1434                                        &IoStatusBlock);
1435     if (!Irp)
1436     {
1437         /* The IRP failed, fail the request as well */
1438         ObDereferenceObject(AttachedDevice);
1439         return STATUS_INSUFFICIENT_RESOURCES;
1440     }
1441 
1442     /* Set default status */
1443     Irp->IoStatus.Information = 0;
1444     Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
1445 
1446     /* Get a stack location in this IRP */
1447     IoStackLocation = IoGetNextIrpStackLocation(Irp);
1448     ASSERT(IoStackLocation);
1449 
1450     /* Initialize it as a query capabilities IRP, with no completion routine */
1451     RtlZeroMemory(IoStackLocation, sizeof(IO_STACK_LOCATION));
1452     IoStackLocation->MajorFunction = IRP_MJ_PNP;
1453     IoStackLocation->MinorFunction = IRP_MN_QUERY_CAPABILITIES;
1454     IoStackLocation->Parameters.DeviceCapabilities.Capabilities = DeviceCapability;
1455     IoSetCompletionRoutine(Irp, NULL, NULL, FALSE, FALSE, FALSE);
1456 
1457     /* Send the IOCTL to the driver */
1458     Status = IoCallDriver(AttachedDevice, Irp);
1459     if (Status == STATUS_PENDING)
1460     {
1461         /* Wait for a response and update the actual status */
1462         KeWaitForSingleObject(&Event,
1463                               Executive,
1464                               KernelMode,
1465                               FALSE,
1466                               NULL);
1467         Status = Irp->IoStatus.Status;
1468     }
1469 
1470     /* Done, dereference the attached device and return the final result */
1471     ObDereferenceObject(AttachedDevice);
1472     return Status;
1473 }
1474 
1475 NTSTATUS
1476 NTAPI
1477 PciQueryPowerCapabilities(IN PPCI_PDO_EXTENSION PdoExtension,
1478                           IN PDEVICE_CAPABILITIES DeviceCapability)
1479 {
1480     PDEVICE_OBJECT DeviceObject;
1481     NTSTATUS Status;
1482     DEVICE_CAPABILITIES AttachedCaps;
1483     DEVICE_POWER_STATE NewPowerState, DevicePowerState, DeviceWakeLevel, DeviceWakeState;
1484     SYSTEM_POWER_STATE SystemWakeState, DeepestWakeState, CurrentState;
1485 
1486     /* Nothing is known at first */
1487     DeviceWakeState = PowerDeviceUnspecified;
1488     SystemWakeState = DeepestWakeState = PowerSystemUnspecified;
1489 
1490     /* Get the PCI capabilities for the parent PDO */
1491     DeviceObject = PdoExtension->ParentFdoExtension->PhysicalDeviceObject;
1492     Status = PciGetDeviceCapabilities(DeviceObject, &AttachedCaps);
1493     ASSERT(NT_SUCCESS(Status));
1494     if (!NT_SUCCESS(Status)) return Status;
1495 
1496     /* Check if there's not an existing device state for S0 */
1497     if (!AttachedCaps.DeviceState[PowerSystemWorking])
1498     {
1499         /* Set D0<->S0 mapping */
1500         AttachedCaps.DeviceState[PowerSystemWorking] = PowerDeviceD0;
1501     }
1502 
1503     /* Check if there's not an existing device state for S3 */
1504     if (!AttachedCaps.DeviceState[PowerSystemShutdown])
1505     {
1506         /* Set D3<->S3 mapping */
1507         AttachedCaps.DeviceState[PowerSystemShutdown] = PowerDeviceD3;
1508     }
1509 
1510     /* Check for a PDO with broken, or no, power capabilities */
1511     if (PdoExtension->HackFlags & PCI_HACK_NO_PM_CAPS)
1512     {
1513         /* Unknown wake device states */
1514         DeviceCapability->DeviceWake = PowerDeviceUnspecified;
1515         DeviceCapability->SystemWake = PowerSystemUnspecified;
1516 
1517         /* No device state support */
1518         DeviceCapability->DeviceD1 = FALSE;
1519         DeviceCapability->DeviceD2 = FALSE;
1520 
1521         /* No waking from any low-power device state is supported */
1522         DeviceCapability->WakeFromD0 = FALSE;
1523         DeviceCapability->WakeFromD1 = FALSE;
1524         DeviceCapability->WakeFromD2 = FALSE;
1525         DeviceCapability->WakeFromD3 = FALSE;
1526 
1527         /* For the rest, copy whatever the parent PDO had */
1528         RtlCopyMemory(DeviceCapability->DeviceState,
1529                       AttachedCaps.DeviceState,
1530                       sizeof(DeviceCapability->DeviceState));
1531         return STATUS_SUCCESS;
1532     }
1533 
1534     /* The PCI Device has power capabilities, so read which ones are supported */
1535     DeviceCapability->DeviceD1 = PdoExtension->PowerCapabilities.Support.D1;
1536     DeviceCapability->DeviceD2 = PdoExtension->PowerCapabilities.Support.D2;
1537     DeviceCapability->WakeFromD0 = PdoExtension->PowerCapabilities.Support.PMED0;
1538     DeviceCapability->WakeFromD1 = PdoExtension->PowerCapabilities.Support.PMED1;
1539     DeviceCapability->WakeFromD2 = PdoExtension->PowerCapabilities.Support.PMED2;
1540 
1541     /* Can the attached device wake from D3? */
1542     if (AttachedCaps.DeviceWake != PowerDeviceD3)
1543     {
1544         /* It can't, so check if this PDO supports hot D3 wake */
1545         DeviceCapability->WakeFromD3 = PdoExtension->PowerCapabilities.Support.PMED3Hot;
1546     }
1547     else
1548     {
1549         /* It can, is this the root bus? */
1550         if (PCI_IS_ROOT_FDO(PdoExtension->ParentFdoExtension))
1551         {
1552             /* This is the root bus, so just check if it supports hot D3 wake */
1553             DeviceCapability->WakeFromD3 = PdoExtension->PowerCapabilities.Support.PMED3Hot;
1554         }
1555         else
1556         {
1557             /* Take the minimums? -- need to check with briang at work */
1558             UNIMPLEMENTED;
1559         }
1560     }
1561 
1562     /* Now loop each system power state to determine its device state mapping */
1563     for (CurrentState = PowerSystemWorking;
1564          CurrentState < PowerSystemMaximum;
1565          CurrentState++)
1566     {
1567         /* Read the current mapping from the attached device */
1568         DevicePowerState = AttachedCaps.DeviceState[CurrentState];
1569         NewPowerState = DevicePowerState;
1570 
1571         /* The attachee supports D1, but this PDO does not */
1572         if ((NewPowerState == PowerDeviceD1) &&
1573             !(PdoExtension->PowerCapabilities.Support.D1))
1574         {
1575             /* Fall back to D2 */
1576             NewPowerState = PowerDeviceD2;
1577         }
1578 
1579         /* The attachee supports D2, but this PDO does not */
1580         if ((NewPowerState == PowerDeviceD2) &&
1581             !(PdoExtension->PowerCapabilities.Support.D2))
1582         {
1583             /* Fall back to D3 */
1584             NewPowerState = PowerDeviceD3;
1585         }
1586 
1587         /* Set the mapping based on the best state supported */
1588         DeviceCapability->DeviceState[CurrentState] = NewPowerState;
1589 
1590         /* Check if sleep states are being processed, and a mapping was found */
1591         if ((CurrentState < PowerSystemHibernate) &&
1592             (NewPowerState != PowerDeviceUnspecified))
1593         {
1594             /* Save this state as being the deepest one found until now */
1595             DeepestWakeState = CurrentState;
1596         }
1597 
1598         /*
1599          * Finally, check if the computed sleep state is within the states that
1600          * this device can wake the system from, and if it's higher or equal to
1601          * the sleep state mapping that came from the attachee, assuming that it
1602          * had a valid mapping to begin with.
1603          *
1604          * It this is the case, then make sure that the computed sleep state is
1605          * matched by the device's ability to actually wake from that state.
1606          *
1607          * For devices that support D3, the PCI device only needs Hot D3 as long
1608          * as the attachee's state is less than D3. Otherwise, if the attachee
1609          * might also be at D3, this would require a Cold D3 wake, so check that
1610          * the device actually support this.
1611          */
1612         if ((CurrentState < AttachedCaps.SystemWake) &&
1613             (NewPowerState >= DevicePowerState) &&
1614             (DevicePowerState != PowerDeviceUnspecified) &&
1615             (((NewPowerState == PowerDeviceD0) && (DeviceCapability->WakeFromD0)) ||
1616              ((NewPowerState == PowerDeviceD1) && (DeviceCapability->WakeFromD1)) ||
1617              ((NewPowerState == PowerDeviceD2) && (DeviceCapability->WakeFromD2)) ||
1618              ((NewPowerState == PowerDeviceD3) &&
1619               (PdoExtension->PowerCapabilities.Support.PMED3Hot) &&
1620               ((DevicePowerState < PowerDeviceD3) ||
1621                (PdoExtension->PowerCapabilities.Support.PMED3Cold)))))
1622         {
1623             /* The mapping is valid, so this will be the lowest wake state */
1624             SystemWakeState = CurrentState;
1625             DeviceWakeState = NewPowerState;
1626         }
1627     }
1628 
1629     /* Read the current wake level */
1630     DeviceWakeLevel = PdoExtension->PowerState.DeviceWakeLevel;
1631 
1632     /* Check if the attachee's wake levels are valid, and the PDO's is higher */
1633     if ((AttachedCaps.SystemWake != PowerSystemUnspecified) &&
1634         (AttachedCaps.DeviceWake != PowerDeviceUnspecified) &&
1635         (DeviceWakeLevel != PowerDeviceUnspecified) &&
1636         (DeviceWakeLevel >= AttachedCaps.DeviceWake))
1637     {
1638         /* Inherit the system wake from the attachee, and this PDO's wake level */
1639         DeviceCapability->SystemWake = AttachedCaps.SystemWake;
1640         DeviceCapability->DeviceWake = DeviceWakeLevel;
1641 
1642         /* Now check if the wake level is D0, but the PDO doesn't support it */
1643         if ((DeviceCapability->DeviceWake == PowerDeviceD0) &&
1644             !(DeviceCapability->WakeFromD0))
1645         {
1646             /* Bump to D1 */
1647             DeviceCapability->DeviceWake = PowerDeviceD1;
1648         }
1649 
1650         /* Now check if the wake level is D1, but the PDO doesn't support it */
1651         if ((DeviceCapability->DeviceWake == PowerDeviceD1) &&
1652             !(DeviceCapability->WakeFromD1))
1653         {
1654             /* Bump to D2 */
1655             DeviceCapability->DeviceWake = PowerDeviceD2;
1656         }
1657 
1658         /* Now check if the wake level is D2, but the PDO doesn't support it */
1659         if ((DeviceCapability->DeviceWake == PowerDeviceD2) &&
1660             !(DeviceCapability->WakeFromD2))
1661         {
1662             /* Bump it to D3 */
1663             DeviceCapability->DeviceWake = PowerDeviceD3;
1664         }
1665 
1666         /* Now check if the wake level is D3, but the PDO doesn't support it */
1667         if ((DeviceCapability->DeviceWake == PowerDeviceD3) &&
1668             !(DeviceCapability->WakeFromD3))
1669         {
1670             /* Then no valid wake state exists */
1671             DeviceCapability->DeviceWake = PowerDeviceUnspecified;
1672             DeviceCapability->SystemWake = PowerSystemUnspecified;
1673         }
1674 
1675         /* Check if no valid wake state was found */
1676         if ((DeviceCapability->DeviceWake == PowerDeviceUnspecified) ||
1677             (DeviceCapability->SystemWake == PowerSystemUnspecified))
1678         {
1679             /* Check if one was computed earlier */
1680             if ((SystemWakeState != PowerSystemUnspecified) &&
1681                 (DeviceWakeState != PowerDeviceUnspecified))
1682             {
1683                 /* Use the wake state that had been computed earlier */
1684                 DeviceCapability->DeviceWake = DeviceWakeState;
1685                 DeviceCapability->SystemWake = SystemWakeState;
1686 
1687                 /* If that state was D3, then the device supports Hot/Cold D3 */
1688                 if (DeviceWakeState == PowerDeviceD3) DeviceCapability->WakeFromD3 = TRUE;
1689             }
1690         }
1691 
1692         /*
1693          * Finally, check for off states (lower than S3, such as hibernate) and
1694          * make sure that the device both supports waking from D3 as well as
1695          * supports a Cold wake
1696          */
1697         if ((DeviceCapability->SystemWake > PowerSystemSleeping3) &&
1698             ((DeviceCapability->DeviceWake != PowerDeviceD3) ||
1699              !(PdoExtension->PowerCapabilities.Support.PMED3Cold)))
1700         {
1701             /* It doesn't, so pick the computed lowest wake state from earlier */
1702             DeviceCapability->SystemWake = DeepestWakeState;
1703         }
1704 
1705         /* Set the PCI Specification mandated maximum latencies for transitions */
1706         DeviceCapability->D1Latency = 0;
1707         DeviceCapability->D2Latency = 2;
1708         DeviceCapability->D3Latency = 100;
1709 
1710         /* Sanity check */
1711         ASSERT(DeviceCapability->DeviceState[PowerSystemWorking] == PowerDeviceD0);
1712     }
1713     else
1714     {
1715         /* No valid sleep states, no latencies to worry about */
1716         DeviceCapability->D1Latency = 0;
1717         DeviceCapability->D2Latency = 0;
1718         DeviceCapability->D3Latency = 0;
1719     }
1720 
1721     /* This function always succeeds, even without power management support */
1722     return STATUS_SUCCESS;
1723 }
1724 
1725 NTSTATUS
1726 NTAPI
1727 PciQueryCapabilities(IN PPCI_PDO_EXTENSION PdoExtension,
1728                      IN OUT PDEVICE_CAPABILITIES DeviceCapability)
1729 {
1730     NTSTATUS Status;
1731 
1732     /* A PDO ID is never unique, and its address is its function and device */
1733     DeviceCapability->UniqueID = FALSE;
1734     DeviceCapability->Address = PdoExtension->Slot.u.bits.FunctionNumber |
1735                                 (PdoExtension->Slot.u.bits.DeviceNumber << 16);
1736 
1737     /* Check for host bridges */
1738     if ((PdoExtension->BaseClass == PCI_CLASS_BRIDGE_DEV) &&
1739         (PdoExtension->SubClass == PCI_SUBCLASS_BR_HOST))
1740     {
1741         /* Raw device opens to a host bridge are acceptable */
1742         DeviceCapability->RawDeviceOK = TRUE;
1743     }
1744     else
1745     {
1746         /* Otherwise, other PDOs cannot be directly opened */
1747         DeviceCapability->RawDeviceOK = FALSE;
1748     }
1749 
1750     /* PCI PDOs are pretty fixed things */
1751     DeviceCapability->LockSupported = FALSE;
1752     DeviceCapability->EjectSupported = FALSE;
1753     DeviceCapability->Removable = FALSE;
1754     DeviceCapability->DockDevice = FALSE;
1755 
1756     /* The slot number is stored as a device property, go query it */
1757     PciDetermineSlotNumber(PdoExtension, &DeviceCapability->UINumber);
1758 
1759     /* Finally, query and power capabilities and convert them for PnP usage */
1760     Status = PciQueryPowerCapabilities(PdoExtension, DeviceCapability);
1761 
1762     /* Dump the capabilities if it all worked, and return the status */
1763     if (NT_SUCCESS(Status)) PciDebugDumpQueryCapabilities(DeviceCapability);
1764     return Status;
1765 }
1766 
1767 /* EOF */
1768