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