Sync with trunk (r48545)
[reactos.git] / drivers / bus / pcix / utils.c
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 #define NDEBUG
13 #include <debug.h>
14
15 /* GLOBALS ********************************************************************/
16
17 ULONG PciDebugPortsCount;
18
19 RTL_RANGE_LIST PciIsaBitExclusionList;
20 RTL_RANGE_LIST PciVgaAndIsaBitExclusionList;
21
22 /* FUNCTIONS ******************************************************************/
23
24 BOOLEAN
25 NTAPI
26 PciUnicodeStringStrStr(IN PUNICODE_STRING InputString,
27 IN PCUNICODE_STRING EqualString,
28 IN BOOLEAN CaseInSensitive)
29 {
30 UNICODE_STRING PartialString;
31 LONG EqualChars, TotalChars;
32
33 /* Build a partial string with the smaller substring */
34 PartialString.Length = EqualString->Length;
35 PartialString.MaximumLength = InputString->MaximumLength;;
36 PartialString.Buffer = InputString->Buffer;
37
38 /* Check how many characters that need comparing */
39 EqualChars = 0;
40 TotalChars = (InputString->Length - EqualString->Length) / sizeof(WCHAR);
41
42 /* If the substring is bigger, just fail immediately */
43 if (TotalChars < 0) return FALSE;
44
45 /* Keep checking each character */
46 while (!RtlEqualUnicodeString(EqualString, &PartialString, CaseInSensitive))
47 {
48 /* Continue checking until all the required characters are equal */
49 PartialString.Buffer++;
50 PartialString.MaximumLength -= sizeof(WCHAR);
51 if (++EqualChars > TotalChars) return FALSE;
52 }
53
54 /* The string is equal */
55 return TRUE;
56 }
57
58 BOOLEAN
59 NTAPI
60 PciStringToUSHORT(IN PWCHAR String,
61 OUT PUSHORT Value)
62 {
63 USHORT Short;
64 ULONG Low, High, Length;
65 WCHAR Char;
66
67 /* Initialize everything to zero */
68 Short = 0;
69 Length = 0;
70 while (TRUE)
71 {
72 /* Get the character and set the high byte based on the previous one */
73 Char = *String++;
74 High = 16 * Short;
75
76 /* Check for numbers */
77 if ( Char >= '0' && Char <= '9' )
78 {
79 /* Convert them to a byte */
80 Low = Char - '0';
81 }
82 else if ( Char >= 'A' && Char <= 'F' )
83 {
84 /* Convert upper-case hex letters into a byte */
85 Low = Char - '7';
86 }
87 else if ( Char >= 'a' && Char <= 'f' )
88 {
89 /* Convert lower-case hex letters into a byte */
90 Low = Char - 'W';
91 }
92 else
93 {
94 /* Invalid string, fail the conversion */
95 return FALSE;
96 }
97
98 /* Combine the high and low byte */
99 Short = High | Low;
100
101 /* If 4 letters have been reached, the 16-bit integer should exist */
102 if (++Length >= 4)
103 {
104 /* Return it to the caller */
105 *Value = Short;
106 return TRUE;
107 }
108 }
109 }
110
111 BOOLEAN
112 NTAPI
113 PciIsSuiteVersion(IN USHORT SuiteMask)
114 {
115 ULONGLONG Mask = 0;
116 RTL_OSVERSIONINFOEXW VersionInfo;
117
118 /* Initialize the version information */
119 RtlZeroMemory(&VersionInfo, sizeof(RTL_OSVERSIONINFOEXW));
120 VersionInfo.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW);
121 VersionInfo.wSuiteMask = SuiteMask;
122
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));
126 }
127
128 BOOLEAN
129 NTAPI
130 PciIsDatacenter(VOID)
131 {
132 BOOLEAN Result;
133 PVOID Value;
134 ULONG ResultLength;
135 NTSTATUS Status;
136
137 /* Assume this isn't Datacenter */
138 Result = FALSE;
139
140 /* First, try opening the setup key */
141 Status = PciGetRegistryValue(L"",
142 L"\\REGISTRY\\MACHINE\\SYSTEM\\CurrentControlSet\\Services\\setupdd",
143 0,
144 REG_BINARY,
145 &Value,
146 &ResultLength);
147 if (!NT_SUCCESS(Status))
148 {
149 /* This is not an in-progress Setup boot, so query the suite version */
150 Result = PciIsSuiteVersion(VER_SUITE_DATACENTER);
151 }
152 else
153 {
154 /* This scenario shouldn't happen yet, since SetupDD isn't used */
155 UNIMPLEMENTED;
156 while (TRUE);
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,
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 /* If we found it, break out */
376 if (FoundExtension) break;
377
378 /* Release this device's lock */
379 KeSetEvent(&DeviceExtension->ChildListLock, IO_NO_INCREMENT, FALSE);
380 KeLeaveCriticalRegion();
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(FALSE, "PCI Successfully did the impossible!");
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 (%d 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 /* Initialize the variables before looping */
616 LastWeight = 0;
617 HackFlags = 0;
618 ASSERT(PciHackTable);
619
620 /* Scan the hack table */
621 for (HackEntry = PciHackTable;
622 HackEntry->VendorID != PCI_INVALID_VENDORID;
623 ++HackEntry)
624 {
625 /* Check if there's an entry for this device */
626 if ((HackEntry->DeviceID == DeviceId) &&
627 (HackEntry->VendorID == VendorId))
628 {
629 /* This is a basic match */
630 EntryFlags = HackEntry->Flags;
631 MatchWeight = 1;
632
633 /* Does the entry have revision information? */
634 if (EntryFlags & PCI_HACK_HAS_REVISION_INFO)
635 {
636 /* Check if the revision matches, if so, this is a better match */
637 if (HackEntry->RevisionID != RevisionId) continue;
638 MatchWeight = 3;
639 }
640
641 /* Does the netry have subsystem information? */
642 if (EntryFlags & PCI_HACK_HAS_SUBSYSTEM_INFO)
643 {
644 /* Check if it matches, if so, this is the best possible match */
645 if ((HackEntry->SubVendorID != SubVendorId) ||
646 (HackEntry->SubSystemID != SubSystemId))
647 {
648 continue;
649 }
650 MatchWeight += 4;
651 }
652
653 /* Is this the best match yet? */
654 if (MatchWeight > LastWeight)
655 {
656 /* This is the best match for now, use this as the hack flags */
657 HackFlags = HackEntry->HackFlags;
658 LastWeight = MatchWeight;
659 }
660 }
661 }
662
663 /* Return the best match */
664 return HackFlags;
665 }
666
667 BOOLEAN
668 NTAPI
669 PciIsCriticalDeviceClass(IN UCHAR BaseClass,
670 IN UCHAR SubClass)
671 {
672 /* Check for system or bridge devices */
673 if (BaseClass == PCI_CLASS_BASE_SYSTEM_DEV)
674 {
675 /* Interrupt controlers are critical */
676 return SubClass == PCI_SUBCLASS_SYS_INTERRUPT_CTLR;
677 }
678 else if (BaseClass == PCI_CLASS_BRIDGE_DEV)
679 {
680 /* ISA Bridges are critical */
681 return SubClass == PCI_SUBCLASS_BR_ISA;
682 }
683 else
684 {
685 /* All display controllers are critical */
686 return BaseClass == PCI_CLASS_DISPLAY_CTLR;
687 }
688 }
689
690 PPCI_PDO_EXTENSION
691 NTAPI
692 PciFindPdoByFunction(IN PPCI_FDO_EXTENSION DeviceExtension,
693 IN ULONG FunctionNumber,
694 IN PPCI_COMMON_HEADER PciData)
695 {
696 KIRQL Irql;
697 PPCI_PDO_EXTENSION PdoExtension;
698
699 /* Get the current IRQL when this call was made */
700 Irql = KeGetCurrentIrql();
701
702 /* Is this a low-IRQL call? */
703 if (Irql < DISPATCH_LEVEL)
704 {
705 /* Acquire this device's lock */
706 KeEnterCriticalRegion();
707 KeWaitForSingleObject(&DeviceExtension->ChildListLock,
708 Executive,
709 KernelMode,
710 FALSE,
711 NULL);
712 }
713
714 /* Loop every child PDO */
715 for (PdoExtension = DeviceExtension->ChildPdoList;
716 PdoExtension;
717 PdoExtension = PdoExtension->Next)
718 {
719 /* Find only enumerated PDOs */
720 if (!PdoExtension->ReportedMissing)
721 {
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))
727 {
728 /* This is considered to be the same PDO */
729 break;
730 }
731 }
732 }
733
734 /* Was this a low-IRQL call? */
735 if (Irql < DISPATCH_LEVEL)
736 {
737 /* Release this device's lock */
738 KeSetEvent(&DeviceExtension->ChildListLock, IO_NO_INCREMENT, FALSE);
739 KeLeaveCriticalRegion();
740 }
741
742 /* If the search found something, this is non-NULL, otherwise it's NULL */
743 return PdoExtension;
744 }
745
746 BOOLEAN
747 NTAPI
748 PciIsDeviceOnDebugPath(IN PPCI_PDO_EXTENSION DeviceExtension)
749 {
750 PAGED_CODE();
751
752 /* Check for too many, or no, debug ports */
753 ASSERT(PciDebugPortsCount <= MAX_DEBUGGING_DEVICES_SUPPORTED);
754 if (!PciDebugPortsCount) return FALSE;
755
756 /* eVb has not been able to test such devices yet */
757 UNIMPLEMENTED;
758 while (TRUE);
759 }
760
761 NTSTATUS
762 NTAPI
763 PciGetBiosConfig(IN PPCI_PDO_EXTENSION DeviceExtension,
764 OUT PPCI_COMMON_HEADER PciData)
765 {
766 HANDLE KeyHandle, SubKeyHandle;
767 OBJECT_ATTRIBUTES ObjectAttributes;
768 UNICODE_STRING KeyName, KeyValue;
769 WCHAR Buffer[32];
770 WCHAR DataBuffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + PCI_COMMON_HDR_LENGTH];
771 PKEY_VALUE_PARTIAL_INFORMATION PartialInfo = (PVOID)DataBuffer;
772 NTSTATUS Status;
773 ULONG ResultLength;
774 PAGED_CODE();
775
776 /* Open the PCI key */
777 Status = IoOpenDeviceRegistryKey(DeviceExtension->ParentFdoExtension->
778 PhysicalDeviceObject,
779 TRUE,
780 KEY_ALL_ACCESS,
781 &KeyHandle);
782 if (!NT_SUCCESS(Status)) return Status;
783
784 /* Create a volatile BIOS configuration key */
785 RtlInitUnicodeString(&KeyName, L"BiosConfig");
786 InitializeObjectAttributes(&ObjectAttributes,
787 &KeyName,
788 OBJ_KERNEL_HANDLE,
789 KeyHandle,
790 NULL);
791 Status = ZwCreateKey(&SubKeyHandle,
792 KEY_READ,
793 &ObjectAttributes,
794 0,
795 NULL,
796 REG_OPTION_VOLATILE,
797 NULL);
798 ZwClose(KeyHandle);
799 if (!NT_SUCCESS(Status)) return Status;
800
801 /* Create the key value based on the device and function number */
802 swprintf(Buffer,
803 L"DEV_%02x&FUN_%02x",
804 DeviceExtension->Slot.u.bits.DeviceNumber,
805 DeviceExtension->Slot.u.bits.FunctionNumber);
806 RtlInitUnicodeString(&KeyValue, Buffer);
807
808 /* Query the value information (PCI BIOS configuration header) */
809 Status = ZwQueryValueKey(SubKeyHandle,
810 &KeyValue,
811 KeyValuePartialInformation,
812 PartialInfo,
813 sizeof(DataBuffer),
814 &ResultLength);
815 ZwClose(SubKeyHandle);
816 if (!NT_SUCCESS(Status)) return Status;
817
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);
821 return Status;
822 }
823
824 NTSTATUS
825 NTAPI
826 PciSaveBiosConfig(IN PPCI_PDO_EXTENSION DeviceExtension,
827 IN PPCI_COMMON_HEADER PciData)
828 {
829 HANDLE KeyHandle, SubKeyHandle;
830 OBJECT_ATTRIBUTES ObjectAttributes;
831 UNICODE_STRING KeyName, KeyValue;
832 WCHAR Buffer[32];
833 NTSTATUS Status;
834 PAGED_CODE();
835
836 /* Open the PCI key */
837 Status = IoOpenDeviceRegistryKey(DeviceExtension->ParentFdoExtension->
838 PhysicalDeviceObject,
839 TRUE,
840 KEY_READ | KEY_WRITE,
841 &KeyHandle);
842 if (!NT_SUCCESS(Status)) return Status;
843
844 /* Create a volatile BIOS configuration key */
845 RtlInitUnicodeString(&KeyName, L"BiosConfig");
846 InitializeObjectAttributes(&ObjectAttributes,
847 &KeyName,
848 OBJ_KERNEL_HANDLE,
849 KeyHandle,
850 NULL);
851 Status = ZwCreateKey(&SubKeyHandle,
852 KEY_READ | KEY_WRITE,
853 &ObjectAttributes,
854 0,
855 NULL,
856 REG_OPTION_VOLATILE,
857 NULL);
858 ZwClose(KeyHandle);
859 if (!NT_SUCCESS(Status)) return Status;
860
861 /* Create the key value based on the device and function number */
862 swprintf(Buffer,
863 L"DEV_%02x&FUN_%02x",
864 DeviceExtension->Slot.u.bits.DeviceNumber,
865 DeviceExtension->Slot.u.bits.FunctionNumber);
866 RtlInitUnicodeString(&KeyValue, Buffer);
867
868 /* Set the value data (the PCI BIOS configuration header) */
869 Status = ZwSetValueKey(SubKeyHandle,
870 &KeyValue,
871 0,
872 REG_BINARY,
873 PciData,
874 PCI_COMMON_HDR_LENGTH);
875 ZwClose(SubKeyHandle);
876 return Status;
877 }
878
879 UCHAR
880 NTAPI
881 PciReadDeviceCapability(IN PPCI_PDO_EXTENSION DeviceExtension,
882 IN UCHAR Offset,
883 IN ULONG CapabilityId,
884 OUT PPCI_CAPABILITIES_HEADER Buffer,
885 IN ULONG Length)
886 {
887 ULONG CapabilityCount = 0;
888
889 /* If the device has no capabilility list, fail */
890 if (!Offset) return 0;
891
892 /* Validate a PDO with capabilities, a valid buffer, and a valid length */
893 ASSERT(DeviceExtension->ExtensionType == PciPdoExtensionType);
894 ASSERT(DeviceExtension->CapabilitiesPtr != 0);
895 ASSERT(Buffer);
896 ASSERT(Length >= sizeof(PCI_CAPABILITIES_HEADER));
897
898 /* Loop all capabilities */
899 while (Offset)
900 {
901 /* Make sure the pointer is spec-aligned and spec-sized */
902 ASSERT((Offset >= PCI_COMMON_HDR_LENGTH) && ((Offset & 0x3) == 0));
903
904 /* Read the capability header */
905 PciReadDeviceConfig(DeviceExtension,
906 Buffer,
907 Offset,
908 sizeof(PCI_CAPABILITIES_HEADER));
909
910 /* Check if this is the capability being looked up */
911 if ((Buffer->CapabilityID == CapabilityId) || !(CapabilityId))
912 {
913 /* Check if was at a valid offset and length */
914 if ((Offset) && (Length > sizeof(PCI_CAPABILITIES_HEADER)))
915 {
916 /* Sanity check */
917 ASSERT(Length <= (sizeof(PCI_COMMON_CONFIG) - Offset));
918
919 /* Now read the whole capability data into the buffer */
920 PciReadDeviceConfig(DeviceExtension,
921 (PVOID)((ULONG_PTR)Buffer +
922 sizeof(PCI_CAPABILITIES_HEADER)),
923 Offset + sizeof(PCI_CAPABILITIES_HEADER),
924 Length - sizeof(PCI_CAPABILITIES_HEADER));
925 }
926
927 /* Return the offset where the capability was found */
928 return Offset;
929 }
930
931 /* Try the next capability instead */
932 CapabilityCount++;
933 Offset = Buffer->Next;
934
935 /* There can't be more than 48 capabilities (256 bytes max) */
936 if (CapabilityCount > 48)
937 {
938 /* Fail, since this is basically a broken PCI device */
939 DPRINT1("PCI device %p capabilities list is broken.\n", DeviceExtension);
940 return 0;
941 }
942 }
943
944 /* Capability wasn't found, fail */
945 return 0;
946 }
947
948 BOOLEAN
949 NTAPI
950 PciCanDisableDecodes(IN PPCI_PDO_EXTENSION DeviceExtension,
951 IN PPCI_COMMON_HEADER Config,
952 IN ULONGLONG HackFlags,
953 IN BOOLEAN ForPowerDown)
954 {
955 UCHAR BaseClass, SubClass;
956 BOOLEAN IsVga;
957
958 /* Is there a device extension or should the PCI header be used? */
959 if (DeviceExtension)
960 {
961 /* Never disable decodes for a debug PCI Device */
962 if (DeviceExtension->OnDebugPath) return FALSE;
963
964 /* Hack flags will be obtained from the extension, not the caller */
965 ASSERT(HackFlags == 0);
966
967 /* Get hacks and classification from the device extension */
968 HackFlags = DeviceExtension->HackFlags;
969 SubClass = DeviceExtension->SubClass;
970 BaseClass = DeviceExtension->BaseClass;
971 }
972 else
973 {
974 /* There must be a PCI header, go read the classification information */
975 ASSERT(Config != NULL);
976 BaseClass = Config->BaseClass;
977 SubClass = Config->SubClass;
978 }
979
980 /* Check for hack flags that prevent disabling the decodes */
981 if (HackFlags & (PCI_HACK_PRESERVE_COMMAND |
982 PCI_HACK_CB_SHARE_CMD_BITS |
983 PCI_HACK_DONT_DISABLE_DECODES))
984 {
985 /* Don't do it */
986 return FALSE;
987 }
988
989 /* Is this a VGA adapter? */
990 if ((BaseClass == PCI_CLASS_DISPLAY_CTLR) &&
991 (SubClass == PCI_SUBCLASS_VID_VGA_CTLR))
992 {
993 /* Never disable decodes if this is for power down */
994 return ForPowerDown;
995 }
996
997 /* Check for legacy devices */
998 if (BaseClass == PCI_CLASS_PRE_20)
999 {
1000 /* Never disable video adapter cards if this is for power down */
1001 if (SubClass == PCI_SUBCLASS_PRE_20_VGA) return ForPowerDown;
1002 }
1003 else if (BaseClass == PCI_CLASS_DISPLAY_CTLR)
1004 {
1005 /* Never disable VGA adapters if this is for power down */
1006 if (SubClass == PCI_SUBCLASS_VID_VGA_CTLR) return ForPowerDown;
1007 }
1008 else if (BaseClass == PCI_CLASS_BRIDGE_DEV)
1009 {
1010 /* Check for legacy bridges */
1011 if ((SubClass == PCI_SUBCLASS_BR_ISA) ||
1012 (SubClass == PCI_SUBCLASS_BR_EISA) ||
1013 (SubClass == PCI_SUBCLASS_BR_MCA) ||
1014 (SubClass == PCI_SUBCLASS_BR_HOST) ||
1015 (SubClass == PCI_SUBCLASS_BR_OTHER))
1016 {
1017 /* Never disable these */
1018 return FALSE;
1019 }
1020 else if ((SubClass == PCI_SUBCLASS_BR_PCI_TO_PCI) ||
1021 (SubClass == PCI_SUBCLASS_BR_CARDBUS))
1022 {
1023 /* This is a supported bridge, but does it have a VGA card? */
1024 if (!DeviceExtension)
1025 {
1026 /* Read the bridge control flag from the PCI header */
1027 IsVga = Config->u.type1.BridgeControl & PCI_ENABLE_BRIDGE_VGA;
1028 }
1029 else
1030 {
1031 /* Read the cached flag in the device extension */
1032 IsVga = DeviceExtension->Dependent.type1.VgaBitSet;
1033 }
1034
1035 /* Never disable VGA adapters if this is for power down */
1036 if (IsVga) return ForPowerDown;
1037 }
1038 }
1039
1040 /* Finally, never disable decodes if there's no power management */
1041 return !(HackFlags & PCI_HACK_NO_PM_CAPS);
1042 }
1043
1044 PCI_DEVICE_TYPES
1045 NTAPI
1046 PciClassifyDeviceType(IN PPCI_PDO_EXTENSION PdoExtension)
1047 {
1048 ASSERT(PdoExtension->ExtensionType == PciPdoExtensionType);
1049
1050 /* Differenriate between devices and bridges */
1051 if (PdoExtension->BaseClass != PCI_CLASS_BRIDGE_DEV) return PciTypeDevice;
1052
1053 /* The PCI Bus driver handles only CardBus and PCI bridges (plus host) */
1054 if (PdoExtension->SubClass == PCI_SUBCLASS_BR_HOST) return PciTypeHostBridge;
1055 if (PdoExtension->SubClass == PCI_SUBCLASS_BR_PCI_TO_PCI) return PciTypePciBridge;
1056 if (PdoExtension->SubClass == PCI_SUBCLASS_BR_CARDBUS) return PciTypeCardbusBridge;
1057
1058 /* Any other kind of bridge is treated like a device */
1059 return PciTypeDevice;
1060 }
1061
1062 ULONG_PTR
1063 NTAPI
1064 PciExecuteCriticalSystemRoutine(IN ULONG_PTR IpiContext)
1065 {
1066 PPCI_IPI_CONTEXT Context = (PPCI_IPI_CONTEXT)IpiContext;
1067
1068 /* Check if the IPI is already running */
1069 if (!InterlockedDecrement(&Context->RunCount))
1070 {
1071 /* Nope, this is the first instance, so execute the IPI function */
1072 Context->Function(Context->PdoExtension, Context->Context);
1073
1074 /* Notify anyone that was spinning that they can stop now */
1075 Context->Barrier = 0;
1076 }
1077 else
1078 {
1079 /* Spin until it has finished running */
1080 while (Context->Barrier);
1081 }
1082
1083 /* Done */
1084 return 0;
1085 }
1086
1087 BOOLEAN
1088 NTAPI
1089 PciIsSlotPresentInParentMethod(IN PPCI_PDO_EXTENSION PdoExtension,
1090 IN ULONG Method)
1091 {
1092 BOOLEAN FoundSlot;
1093 PACPI_METHOD_ARGUMENT Argument;
1094 ACPI_EVAL_INPUT_BUFFER InputBuffer;
1095 PACPI_EVAL_OUTPUT_BUFFER OutputBuffer;
1096 ULONG i, Length;
1097 NTSTATUS Status;
1098 PAGED_CODE();
1099
1100 /* Assume slot is not part of the parent method */
1101 FoundSlot = FALSE;
1102
1103 /* Allocate a 2KB buffer for the method return parameters */
1104 Length = sizeof(ACPI_EVAL_OUTPUT_BUFFER) + 2048;
1105 OutputBuffer = ExAllocatePoolWithTag(PagedPool, Length, 'BicP');
1106 if (OutputBuffer)
1107 {
1108 /* Clear out the output buffer */
1109 RtlZeroMemory(OutputBuffer, Length);
1110
1111 /* Initialize the input buffer with the method requested */
1112 InputBuffer.Signature = 0;
1113 *(PULONG)InputBuffer.MethodName = Method;
1114 InputBuffer.Signature = ACPI_EVAL_INPUT_BUFFER_SIGNATURE;
1115
1116 /* Send it to the ACPI driver */
1117 Status = PciSendIoctl(PdoExtension->ParentFdoExtension->PhysicalDeviceObject,
1118 IOCTL_ACPI_EVAL_METHOD,
1119 &InputBuffer,
1120 sizeof(ACPI_EVAL_INPUT_BUFFER),
1121 OutputBuffer,
1122 Length);
1123 if (NT_SUCCESS(Status))
1124 {
1125 /* Scan all output arguments */
1126 for (i = 0; i < OutputBuffer->Count; i++)
1127 {
1128 /* Make sure it's an integer */
1129 Argument = &OutputBuffer->Argument[i];
1130 if (Argument->Type != ACPI_METHOD_ARGUMENT_INTEGER) continue;
1131
1132 /* Check if the argument matches this PCI slot structure */
1133 if (Argument->Argument == ((PdoExtension->Slot.u.bits.DeviceNumber) |
1134 ((PdoExtension->Slot.u.bits.FunctionNumber) << 16)))
1135 {
1136 /* This slot has been found, return it */
1137 FoundSlot = TRUE;
1138 break;
1139 }
1140 }
1141 }
1142
1143 /* Finished with the buffer, free it */
1144 ExFreePoolWithTag(OutputBuffer, 0);
1145 }
1146
1147 /* Return if the slot was found */
1148 return FoundSlot;
1149 }
1150
1151 ULONG
1152 NTAPI
1153 PciGetLengthFromBar(IN ULONG Bar)
1154 {
1155 ULONG Length;
1156
1157 /* I/O addresses vs. memory addresses start differently due to alignment */
1158 Length = 1 << ((Bar & PCI_ADDRESS_IO_SPACE) ? 2 : 4);
1159
1160 /* Keep going until a set bit */
1161 while (!(Length & Bar) && (Length)) Length <<= 1;
1162
1163 /* Return the length (might be 0 on 64-bit because it's the low-word) */
1164 if ((Bar & PCI_ADDRESS_MEMORY_TYPE_MASK) != PCI_TYPE_64BIT) ASSERT(Length);
1165 return Length;
1166 }
1167
1168 BOOLEAN
1169 NTAPI
1170 PciCreateIoDescriptorFromBarLimit(PIO_RESOURCE_DESCRIPTOR ResourceDescriptor,
1171 IN PULONG BarArray,
1172 IN BOOLEAN Rom)
1173 {
1174 ULONG CurrentBar, BarLength, BarMask;
1175 BOOLEAN Is64BitBar = FALSE;
1176
1177 /* Check if the BAR is nor I/O nor memory */
1178 CurrentBar = BarArray[0];
1179 if (!(CurrentBar & ~PCI_ADDRESS_IO_SPACE))
1180 {
1181 /* Fail this descriptor */
1182 ResourceDescriptor->Type = CmResourceTypeNull;
1183 return FALSE;
1184 }
1185
1186 /* Set default flag and clear high words */
1187 ResourceDescriptor->Flags = 0;
1188 ResourceDescriptor->u.Generic.MaximumAddress.HighPart = 0;
1189 ResourceDescriptor->u.Generic.MinimumAddress.LowPart = 0;
1190 ResourceDescriptor->u.Generic.MinimumAddress.HighPart = 0;
1191
1192 /* Check for ROM Address */
1193 if (Rom)
1194 {
1195 /* Clean up the BAR to get just the address */
1196 CurrentBar &= PCI_ADDRESS_ROM_ADDRESS_MASK;
1197 if (!CurrentBar)
1198 {
1199 /* Invalid ar, fail this descriptor */
1200 ResourceDescriptor->Type = CmResourceTypeNull;
1201 return FALSE;
1202 }
1203
1204 /* ROM Addresses are always read only */
1205 ResourceDescriptor->Flags = CM_RESOURCE_MEMORY_READ_ONLY;
1206 }
1207
1208 /* Compute the length, assume it's the alignment for now */
1209 BarLength = PciGetLengthFromBar(CurrentBar);
1210 ResourceDescriptor->u.Generic.Length = BarLength;
1211 ResourceDescriptor->u.Generic.Alignment = BarLength;
1212
1213 /* Check what kind of BAR this is */
1214 if (CurrentBar & PCI_ADDRESS_IO_SPACE)
1215 {
1216 /* Use correct mask to decode the address */
1217 BarMask = PCI_ADDRESS_IO_ADDRESS_MASK;
1218
1219 /* Set this as an I/O Port descriptor */
1220 ResourceDescriptor->Type = CmResourceTypePort;
1221 ResourceDescriptor->Flags = CM_RESOURCE_PORT_IO;
1222 }
1223 else
1224 {
1225 /* Use correct mask to decode the address */
1226 BarMask = PCI_ADDRESS_MEMORY_ADDRESS_MASK;
1227
1228 /* Set this as a memory descriptor */
1229 ResourceDescriptor->Type = CmResourceTypeMemory;
1230
1231 /* Check if it's 64-bit or 20-bit decode */
1232 if ((CurrentBar & PCI_ADDRESS_MEMORY_TYPE_MASK) == PCI_TYPE_64BIT)
1233 {
1234 /* The next BAR has the high word, read it */
1235 ResourceDescriptor->u.Port.MaximumAddress.HighPart = BarArray[1];
1236 Is64BitBar = TRUE;
1237 }
1238 else if ((CurrentBar & PCI_ADDRESS_MEMORY_TYPE_MASK) == PCI_TYPE_20BIT)
1239 {
1240 /* Use the correct mask to decode the address */
1241 BarMask = ~0xFFF0000F;
1242 }
1243
1244 /* Check if the BAR is listed as prefetchable memory */
1245 if (CurrentBar & PCI_ADDRESS_MEMORY_PREFETCHABLE)
1246 {
1247 /* Mark the descriptor in the same way */
1248 ResourceDescriptor->Flags |= CM_RESOURCE_MEMORY_PREFETCHABLE;
1249 }
1250 }
1251
1252 /* Now write down the maximum address based on the base + length */
1253 ResourceDescriptor->u.Port.MaximumAddress.QuadPart = (CurrentBar & BarMask) +
1254 BarLength - 1;
1255
1256 /* Return if this is a 64-bit BAR, so the loop code knows to skip the next one */
1257 return Is64BitBar;
1258 }
1259
1260 VOID
1261 NTAPI
1262 PciDecodeEnable(IN PPCI_PDO_EXTENSION PdoExtension,
1263 IN BOOLEAN Enable,
1264 OUT PUSHORT Command)
1265 {
1266 USHORT CommandValue;
1267
1268 /*
1269 * If decodes are being disabled, make sure it's allowed, and in both cases,
1270 * make sure that a hackflag isn't preventing touching the decodes at all.
1271 */
1272 if (((Enable) || (PciCanDisableDecodes(PdoExtension, 0, 0, 0))) &&
1273 !(PdoExtension->HackFlags & PCI_HACK_PRESERVE_COMMAND))
1274 {
1275 /* Did the caller already have a command word? */
1276 if (Command)
1277 {
1278 /* Use the caller's */
1279 CommandValue = *Command;
1280 }
1281 else
1282 {
1283 /* Otherwise, read the current command */
1284 PciReadDeviceConfig(PdoExtension,
1285 &Command,
1286 FIELD_OFFSET(PCI_COMMON_HEADER, Command),
1287 sizeof(USHORT));
1288 }
1289
1290 /* Turn off decodes by default */
1291 CommandValue &= ~(PCI_ENABLE_IO_SPACE |
1292 PCI_ENABLE_MEMORY_SPACE |
1293 PCI_ENABLE_BUS_MASTER);
1294
1295 /* If requested, enable the decodes that were enabled at init time */
1296 if (Enable) CommandValue |= PdoExtension->CommandEnables &
1297 (PCI_ENABLE_IO_SPACE |
1298 PCI_ENABLE_MEMORY_SPACE |
1299 PCI_ENABLE_BUS_MASTER);
1300
1301 /* Update the command word */
1302 PciWriteDeviceConfig(PdoExtension,
1303 &CommandValue,
1304 FIELD_OFFSET(PCI_COMMON_HEADER, Command),
1305 sizeof(USHORT));
1306 }
1307 }
1308
1309 /* EOF */