1 /* 2 * PROJECT: ReactOS PCI Bus Driver 3 * LICENSE: BSD - See COPYING.ARM in the top level directory 4 * FILE: drivers/bus/pci/enum.c 5 * PURPOSE: PCI Bus/Device Enumeration 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 PIO_RESOURCE_REQUIREMENTS_LIST PciZeroIoResourceRequirements; 19 20 PCI_CONFIGURATOR PciConfigurators[] = 21 { 22 { 23 Device_MassageHeaderForLimitsDetermination, 24 Device_RestoreCurrent, 25 Device_SaveLimits, 26 Device_SaveCurrentSettings, 27 Device_ChangeResourceSettings, 28 Device_GetAdditionalResourceDescriptors, 29 Device_ResetDevice 30 }, 31 { 32 PPBridge_MassageHeaderForLimitsDetermination, 33 PPBridge_RestoreCurrent, 34 PPBridge_SaveLimits, 35 PPBridge_SaveCurrentSettings, 36 PPBridge_ChangeResourceSettings, 37 PPBridge_GetAdditionalResourceDescriptors, 38 PPBridge_ResetDevice 39 }, 40 { 41 Cardbus_MassageHeaderForLimitsDetermination, 42 Cardbus_RestoreCurrent, 43 Cardbus_SaveLimits, 44 Cardbus_SaveCurrentSettings, 45 Cardbus_ChangeResourceSettings, 46 Cardbus_GetAdditionalResourceDescriptors, 47 Cardbus_ResetDevice 48 } 49 }; 50 51 /* FUNCTIONS ******************************************************************/ 52 53 BOOLEAN 54 NTAPI 55 PciComputeNewCurrentSettings(IN PPCI_PDO_EXTENSION PdoExtension, 56 IN PCM_RESOURCE_LIST ResourceList) 57 { 58 PCM_PARTIAL_RESOURCE_DESCRIPTOR Partial, InterruptResource; 59 PCM_PARTIAL_RESOURCE_DESCRIPTOR BaseResource, CurrentDescriptor; 60 PCM_PARTIAL_RESOURCE_DESCRIPTOR PreviousDescriptor; 61 CM_PARTIAL_RESOURCE_DESCRIPTOR ResourceArray[7]; 62 PCM_FULL_RESOURCE_DESCRIPTOR FullList; 63 BOOLEAN DrainPartial, RangeChange; 64 ULONG i, j; 65 PPCI_FUNCTION_RESOURCES PciResources; 66 PAGED_CODE(); 67 68 /* Make sure we have either no resources, or at least one */ 69 ASSERT((ResourceList == NULL) || (ResourceList->Count == 1)); 70 71 /* Initialize no partial, interrupt descriptor, or range change */ 72 Partial = NULL; 73 InterruptResource = NULL; 74 RangeChange = FALSE; 75 76 /* Check if there's not actually any resources */ 77 if (!(ResourceList) || !(ResourceList->Count)) 78 { 79 /* Then just return the hardware update state */ 80 return PdoExtension->UpdateHardware; 81 } 82 83 /* Print the new specified resource list */ 84 PciDebugPrintCmResList(ResourceList); 85 86 /* Clear the temporary resource array */ 87 for (i = 0; i < 7; i++) ResourceArray[i].Type = CmResourceTypeNull; 88 89 /* Loop the full resource descriptor */ 90 FullList = ResourceList->List; 91 for (i = 0; i < ResourceList->Count; i++) 92 { 93 /* Initialize loop variables */ 94 DrainPartial = FALSE; 95 BaseResource = NULL; 96 97 /* Loop the partial descriptors */ 98 Partial = FullList->PartialResourceList.PartialDescriptors; 99 for (j = 0; j < FullList->PartialResourceList.Count; j++) 100 { 101 /* Check if we were supposed to drain a partial due to device data */ 102 if (DrainPartial) 103 { 104 /* Draining complete, move on to the next descriptor then */ 105 DrainPartial--; 106 continue; 107 } 108 109 /* Check what kind of descriptor this was */ 110 switch (Partial->Type) 111 { 112 /* Base BAR resources */ 113 case CmResourceTypePort: 114 case CmResourceTypeMemory: 115 116 /* Set it as the base */ 117 ASSERT(BaseResource == NULL); 118 BaseResource = Partial; 119 break; 120 121 /* Interrupt resource */ 122 case CmResourceTypeInterrupt: 123 124 /* Make sure it's a compatible (and the only) PCI interrupt */ 125 ASSERT(InterruptResource == NULL); 126 ASSERT(Partial->u.Interrupt.Level == Partial->u.Interrupt.Vector); 127 InterruptResource = Partial; 128 129 /* Only 255 interrupts on x86/x64 hardware */ 130 if (Partial->u.Interrupt.Level < 256) 131 { 132 /* Use the passed interrupt line */ 133 PdoExtension->AdjustedInterruptLine = Partial->u.Interrupt.Level; 134 } 135 else 136 { 137 /* Invalid vector, so ignore it */ 138 PdoExtension->AdjustedInterruptLine = 0; 139 } 140 141 break; 142 143 /* Check for specific device data */ 144 case CmResourceTypeDevicePrivate: 145 146 /* Check what kind of data this was */ 147 switch (Partial->u.DevicePrivate.Data[0]) 148 { 149 /* Not used in the driver yet */ 150 case 1: 151 UNIMPLEMENTED_DBGBREAK(); 152 break; 153 154 /* Not used in the driver yet */ 155 case 2: 156 UNIMPLEMENTED_DBGBREAK(); 157 break; 158 159 /* A drain request */ 160 case 3: 161 /* Shouldn't be a base resource, this is a drain */ 162 ASSERT(BaseResource == NULL); 163 DrainPartial = Partial->u.DevicePrivate.Data[1]; 164 ASSERT(DrainPartial == TRUE); 165 break; 166 } 167 break; 168 } 169 170 /* Move to the next descriptor */ 171 Partial = CmiGetNextPartialDescriptor(Partial); 172 } 173 174 /* We should be starting a new list now */ 175 ASSERT(BaseResource == NULL); 176 FullList = (PVOID)Partial; 177 } 178 179 /* Check the current assigned PCI resources */ 180 PciResources = PdoExtension->Resources; 181 if (!PciResources) return FALSE; 182 183 //if... // MISSING CODE 184 UNIMPLEMENTED; 185 DPRINT1("Missing sanity checking code!\n"); 186 187 /* Loop all the PCI function resources */ 188 for (i = 0; i < 7; i++) 189 { 190 /* Get the current function resource descriptor, and the new one */ 191 CurrentDescriptor = &PciResources->Current[i]; 192 Partial = &ResourceArray[i]; 193 194 /* Previous is current during the first loop iteration */ 195 PreviousDescriptor = &PciResources->Current[(i == 0) ? (0) : (i - 1)]; 196 197 /* Check if this new descriptor is different than the old one */ 198 if (((Partial->Type != CurrentDescriptor->Type) || 199 (Partial->Type != CmResourceTypeNull)) && 200 ((Partial->u.Generic.Start.QuadPart != 201 CurrentDescriptor->u.Generic.Start.QuadPart) || 202 (Partial->u.Generic.Length != CurrentDescriptor->u.Generic.Length))) 203 { 204 /* Record a change */ 205 RangeChange = TRUE; 206 207 /* Was there a range before? */ 208 if (CurrentDescriptor->Type != CmResourceTypeNull) 209 { 210 /* Print it */ 211 DbgPrint(" Old range-\n"); 212 PciDebugPrintPartialResource(CurrentDescriptor); 213 } 214 else 215 { 216 /* There was no range */ 217 DbgPrint(" Previously unset range\n"); 218 } 219 220 /* Print new one */ 221 DbgPrint(" changed to\n"); 222 PciDebugPrintPartialResource(Partial); 223 224 /* Update to new range */ 225 CurrentDescriptor->Type = Partial->Type; 226 PreviousDescriptor->u.Generic.Start = Partial->u.Generic.Start; 227 PreviousDescriptor->u.Generic.Length = Partial->u.Generic.Length; 228 CurrentDescriptor = PreviousDescriptor; 229 } 230 } 231 232 /* Either the hardware was updated, or a resource range changed */ 233 return ((RangeChange) || (PdoExtension->UpdateHardware)); 234 } 235 236 VOID 237 NTAPI 238 PcipUpdateHardware(IN PVOID Context, 239 IN PVOID Context2) 240 { 241 PPCI_PDO_EXTENSION PdoExtension = Context; 242 PPCI_COMMON_HEADER PciData = Context2; 243 244 /* Check if we're allowed to disable decodes */ 245 PciData->Command = PdoExtension->CommandEnables; 246 if (!(PdoExtension->HackFlags & PCI_HACK_PRESERVE_COMMAND)) 247 { 248 /* Disable all decodes */ 249 PciData->Command &= ~(PCI_ENABLE_IO_SPACE | 250 PCI_ENABLE_MEMORY_SPACE | 251 PCI_ENABLE_BUS_MASTER | 252 PCI_ENABLE_WRITE_AND_INVALIDATE); 253 } 254 255 /* Update the device configuration */ 256 PciData->Status = 0; 257 PciWriteDeviceConfig(PdoExtension, PciData, 0, PCI_COMMON_HDR_LENGTH); 258 259 /* Turn decodes back on */ 260 PciDecodeEnable(PdoExtension, TRUE, &PdoExtension->CommandEnables); 261 } 262 263 VOID 264 NTAPI 265 PciUpdateHardware(IN PPCI_PDO_EXTENSION PdoExtension, 266 IN PPCI_COMMON_HEADER PciData) 267 { 268 PCI_IPI_CONTEXT Context; 269 270 /* Check for critical devices and PCI Debugging devices */ 271 if ((PdoExtension->HackFlags & PCI_HACK_CRITICAL_DEVICE) || 272 (PdoExtension->OnDebugPath)) 273 { 274 /* Build the context and send an IPI */ 275 Context.RunCount = 1; 276 Context.Barrier = 1; 277 Context.Context = PciData; 278 Context.Function = PcipUpdateHardware; 279 Context.DeviceExtension = PdoExtension; 280 KeIpiGenericCall(PciExecuteCriticalSystemRoutine, (ULONG_PTR)&Context); 281 } 282 else 283 { 284 /* Just to the update inline */ 285 PcipUpdateHardware(PdoExtension, PciData); 286 } 287 } 288 289 PIO_RESOURCE_REQUIREMENTS_LIST 290 NTAPI 291 PciAllocateIoRequirementsList(IN ULONG Count, 292 IN ULONG BusNumber, 293 IN ULONG SlotNumber) 294 { 295 SIZE_T Size; 296 PIO_RESOURCE_REQUIREMENTS_LIST RequirementsList; 297 298 /* Calculate the final size of the list, including each descriptor */ 299 Size = sizeof(IO_RESOURCE_REQUIREMENTS_LIST); 300 if (Count > 1) Size = sizeof(IO_RESOURCE_DESCRIPTOR) * (Count - 1) + 301 sizeof(IO_RESOURCE_REQUIREMENTS_LIST); 302 303 /* Allocate the list */ 304 RequirementsList = ExAllocatePoolWithTag(PagedPool, Size, 'BicP'); 305 if (!RequirementsList) return NULL; 306 307 /* Initialize it */ 308 RtlZeroMemory(RequirementsList, Size); 309 RequirementsList->AlternativeLists = 1; 310 RequirementsList->BusNumber = BusNumber; 311 RequirementsList->SlotNumber = SlotNumber; 312 RequirementsList->InterfaceType = PCIBus; 313 RequirementsList->ListSize = Size; 314 RequirementsList->List[0].Count = Count; 315 RequirementsList->List[0].Version = 1; 316 RequirementsList->List[0].Revision = 1; 317 318 /* Return it */ 319 return RequirementsList; 320 } 321 322 PCM_RESOURCE_LIST 323 NTAPI 324 PciAllocateCmResourceList(IN ULONG Count, 325 IN ULONG BusNumber) 326 { 327 SIZE_T Size; 328 PCM_RESOURCE_LIST ResourceList; 329 330 /* Calculate the final size of the list, including each descriptor */ 331 Size = sizeof(CM_RESOURCE_LIST); 332 if (Count > 1) Size = sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR) * (Count - 1) + 333 sizeof(CM_RESOURCE_LIST); 334 335 /* Allocate the list */ 336 ResourceList = ExAllocatePoolWithTag(PagedPool, Size, 'BicP'); 337 if (!ResourceList) return NULL; 338 339 /* Initialize it */ 340 RtlZeroMemory(ResourceList, Size); 341 ResourceList->Count = 1; 342 ResourceList->List[0].BusNumber = BusNumber; 343 ResourceList->List[0].InterfaceType = PCIBus; 344 ResourceList->List[0].PartialResourceList.Version = 1; 345 ResourceList->List[0].PartialResourceList.Revision = 1; 346 ResourceList->List[0].PartialResourceList.Count = Count; 347 348 /* Return it */ 349 return ResourceList; 350 } 351 352 NTSTATUS 353 NTAPI 354 PciQueryResources(IN PPCI_PDO_EXTENSION PdoExtension, 355 OUT PCM_RESOURCE_LIST *Buffer) 356 { 357 PPCI_FUNCTION_RESOURCES PciResources; 358 BOOLEAN HaveVga, HaveMemSpace, HaveIoSpace; 359 USHORT BridgeControl, PciCommand; 360 ULONG Count, i; 361 PCM_PARTIAL_RESOURCE_DESCRIPTOR Partial, Resource, LastResource; 362 PCM_RESOURCE_LIST ResourceList; 363 UCHAR InterruptLine; 364 PAGED_CODE(); 365 366 /* Assume failure */ 367 Count = 0; 368 HaveVga = FALSE; 369 *Buffer = NULL; 370 371 /* Make sure there's some resources to query */ 372 PciResources = PdoExtension->Resources; 373 if (!PciResources) return STATUS_SUCCESS; 374 375 /* Read the decodes */ 376 PciReadDeviceConfig(PdoExtension, 377 &PciCommand, 378 FIELD_OFFSET(PCI_COMMON_HEADER, Command), 379 sizeof(USHORT)); 380 381 /* Check which ones are turned on */ 382 HaveIoSpace = PciCommand & PCI_ENABLE_IO_SPACE; 383 HaveMemSpace = PciCommand & PCI_ENABLE_MEMORY_SPACE; 384 385 /* Loop maximum possible descriptors */ 386 for (i = 0; i < 7; i++) 387 { 388 /* Check if the decode for this descriptor is actually turned on */ 389 Partial = &PciResources->Current[i]; 390 if (((HaveMemSpace) && (Partial->Type == CmResourceTypeMemory)) || 391 ((HaveIoSpace) && (Partial->Type == CmResourceTypePort))) 392 { 393 /* One more fully active descriptor */ 394 Count++; 395 } 396 } 397 398 /* If there's an interrupt pin associated, check at least one decode is on */ 399 if ((PdoExtension->InterruptPin) && ((HaveMemSpace) || (HaveIoSpace))) 400 { 401 /* Read the interrupt line for the pin, add a descriptor if it's valid */ 402 InterruptLine = PdoExtension->AdjustedInterruptLine; 403 if ((InterruptLine) && (InterruptLine != -1)) Count++; 404 } 405 406 /* Check for PCI bridge */ 407 if (PdoExtension->HeaderType == PCI_BRIDGE_TYPE) 408 { 409 /* Read bridge settings, check if VGA is present */ 410 PciReadDeviceConfig(PdoExtension, 411 &BridgeControl, 412 FIELD_OFFSET(PCI_COMMON_HEADER, u.type1.BridgeControl), 413 sizeof(USHORT)); 414 if (BridgeControl & PCI_ENABLE_BRIDGE_VGA) 415 { 416 /* Remember for later */ 417 HaveVga = TRUE; 418 419 /* One memory descriptor for 0xA0000, plus the two I/O port ranges */ 420 if (HaveMemSpace) Count++; 421 if (HaveIoSpace) Count += 2; 422 } 423 } 424 425 /* If there's no descriptors in use, there's no resources, so return */ 426 if (!Count) return STATUS_SUCCESS; 427 428 /* Allocate a resource list to hold the resources */ 429 ResourceList = PciAllocateCmResourceList(Count, 430 PdoExtension->ParentFdoExtension->BaseBus); 431 if (!ResourceList) return STATUS_INSUFFICIENT_RESOURCES; 432 433 /* This is where the descriptors will be copied into */ 434 Resource = ResourceList->List[0].PartialResourceList.PartialDescriptors; 435 LastResource = Resource + Count + 1; 436 437 /* Loop maximum possible descriptors */ 438 for (i = 0; i < 7; i++) 439 { 440 /* Check if the decode for this descriptor is actually turned on */ 441 Partial = &PciResources->Current[i]; 442 if (((HaveMemSpace) && (Partial->Type == CmResourceTypeMemory)) || 443 ((HaveIoSpace) && (Partial->Type == CmResourceTypePort))) 444 { 445 /* Copy the descriptor into the resource list */ 446 *Resource++ = *Partial; 447 } 448 } 449 450 /* Check if earlier the code detected this was a PCI bridge with VGA on it */ 451 if (HaveVga) 452 { 453 /* Are the memory decodes enabled? */ 454 if (HaveMemSpace) 455 { 456 /* Build a memory descriptor for a 128KB framebuffer at 0xA0000 */ 457 Resource->Flags = CM_RESOURCE_MEMORY_READ_WRITE; 458 Resource->u.Generic.Start.HighPart = 0; 459 Resource->Type = CmResourceTypeMemory; 460 Resource->u.Generic.Start.LowPart = 0xA0000; 461 Resource->u.Generic.Length = 0x20000; 462 Resource++; 463 } 464 465 /* Are the I/O decodes enabled? */ 466 if (HaveIoSpace) 467 { 468 /* Build an I/O descriptor for the graphic ports at 0x3B0 */ 469 Resource->Type = CmResourceTypePort; 470 Resource->Flags = CM_RESOURCE_PORT_POSITIVE_DECODE | CM_RESOURCE_PORT_10_BIT_DECODE; 471 Resource->u.Port.Start.QuadPart = 0x3B0u; 472 Resource->u.Port.Length = 0xC; 473 Resource++; 474 475 /* Build an I/O descriptor for the graphic ports at 0x3C0 */ 476 Resource->Type = CmResourceTypePort; 477 Resource->Flags = CM_RESOURCE_PORT_POSITIVE_DECODE | CM_RESOURCE_PORT_10_BIT_DECODE; 478 Resource->u.Port.Start.QuadPart = 0x3C0u; 479 Resource->u.Port.Length = 0x20; 480 Resource++; 481 } 482 } 483 484 /* If there's an interrupt pin associated, check at least one decode is on */ 485 if ((PdoExtension->InterruptPin) && ((HaveMemSpace) || (HaveIoSpace))) 486 { 487 /* Read the interrupt line for the pin, check if it's valid */ 488 InterruptLine = PdoExtension->AdjustedInterruptLine; 489 if ((InterruptLine) && (InterruptLine != -1)) 490 { 491 /* Make sure there's still space */ 492 ASSERT(Resource < LastResource); 493 494 /* Add the interrupt descriptor */ 495 Resource->Flags = CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE; 496 Resource->Type = CmResourceTypeInterrupt; 497 Resource->ShareDisposition = CmResourceShareShared; 498 Resource->u.Interrupt.Affinity = -1; 499 Resource->u.Interrupt.Level = InterruptLine; 500 Resource->u.Interrupt.Vector = InterruptLine; 501 } 502 } 503 504 /* Return the resource list */ 505 *Buffer = ResourceList; 506 return STATUS_SUCCESS; 507 } 508 509 NTSTATUS 510 NTAPI 511 PciQueryTargetDeviceRelations(IN PPCI_PDO_EXTENSION PdoExtension, 512 IN OUT PDEVICE_RELATIONS *pDeviceRelations) 513 { 514 PDEVICE_RELATIONS DeviceRelations; 515 PAGED_CODE(); 516 517 /* If there were existing relations, free them */ 518 if (*pDeviceRelations) ExFreePoolWithTag(*pDeviceRelations, 0); 519 520 /* Allocate a new structure for the relations */ 521 DeviceRelations = ExAllocatePoolWithTag(NonPagedPool, 522 sizeof(DEVICE_RELATIONS), 523 'BicP'); 524 if (!DeviceRelations) return STATUS_INSUFFICIENT_RESOURCES; 525 526 /* Only one relation: the PDO */ 527 DeviceRelations->Count = 1; 528 DeviceRelations->Objects[0] = PdoExtension->PhysicalDeviceObject; 529 ObReferenceObject(DeviceRelations->Objects[0]); 530 531 /* Return the new relations */ 532 *pDeviceRelations = DeviceRelations; 533 return STATUS_SUCCESS; 534 } 535 536 NTSTATUS 537 NTAPI 538 PciQueryEjectionRelations(IN PPCI_PDO_EXTENSION PdoExtension, 539 IN OUT PDEVICE_RELATIONS *pDeviceRelations) 540 { 541 UNREFERENCED_PARAMETER(PdoExtension); 542 UNREFERENCED_PARAMETER(pDeviceRelations); 543 544 /* Not yet implemented */ 545 UNIMPLEMENTED_DBGBREAK(); 546 return STATUS_NOT_IMPLEMENTED; 547 } 548 549 NTSTATUS 550 NTAPI 551 PciBuildRequirementsList(IN PPCI_PDO_EXTENSION PdoExtension, 552 IN PPCI_COMMON_HEADER PciData, 553 OUT PIO_RESOURCE_REQUIREMENTS_LIST* Buffer) 554 { 555 PIO_RESOURCE_REQUIREMENTS_LIST RequirementsList; 556 557 UNREFERENCED_PARAMETER(PdoExtension); 558 UNREFERENCED_PARAMETER(PciData); 559 560 { 561 /* There aren't, so use the zero descriptor */ 562 RequirementsList = PciZeroIoResourceRequirements; 563 564 /* Does it actually exist yet? */ 565 if (!PciZeroIoResourceRequirements) 566 { 567 /* Allocate it, and use it for future use */ 568 RequirementsList = PciAllocateIoRequirementsList(0, 0, 0); 569 PciZeroIoResourceRequirements = RequirementsList; 570 if (!PciZeroIoResourceRequirements) return STATUS_INSUFFICIENT_RESOURCES; 571 } 572 573 /* Return the zero requirements list to the caller */ 574 *Buffer = RequirementsList; 575 DPRINT1("PCI - build resource reqs - early out, 0 resources\n"); 576 return STATUS_SUCCESS; 577 } 578 return STATUS_SUCCESS; 579 } 580 581 NTSTATUS 582 NTAPI 583 PciQueryRequirements(IN PPCI_PDO_EXTENSION PdoExtension, 584 IN OUT PIO_RESOURCE_REQUIREMENTS_LIST *RequirementsList) 585 { 586 NTSTATUS Status; 587 PCI_COMMON_HEADER PciHeader; 588 PAGED_CODE(); 589 590 /* Check if the PDO has any resources, or at least an interrupt pin */ 591 if ((PdoExtension->Resources) || (PdoExtension->InterruptPin)) 592 { 593 /* Read the current PCI header */ 594 PciReadDeviceConfig(PdoExtension, &PciHeader, 0, PCI_COMMON_HDR_LENGTH); 595 596 /* Use it to build a list of requirements */ 597 Status = PciBuildRequirementsList(PdoExtension, &PciHeader, RequirementsList); 598 if (!NT_SUCCESS(Status)) return Status; 599 600 /* Is this a Compaq PCI Hotplug Controller (r17) on a PAE system ? */ 601 if ((PciHeader.VendorID == 0xE11) && 602 (PciHeader.DeviceID == 0xA0F7) && 603 (PciHeader.RevisionID == 17) && 604 (ExIsProcessorFeaturePresent(PF_PAE_ENABLED))) 605 { 606 /* Have not tested this on eVb's machine yet */ 607 UNIMPLEMENTED_DBGBREAK(); 608 } 609 610 /* Check if the requirements are actually the zero list */ 611 if (*RequirementsList == PciZeroIoResourceRequirements) 612 { 613 /* A simple NULL will suffice for the PnP Manager */ 614 *RequirementsList = NULL; 615 DPRINT1("Returning NULL requirements list\n"); 616 } 617 else 618 { 619 /* Otherwise, print out the requirements list */ 620 PciDebugPrintIoResReqList(*RequirementsList); 621 } 622 } 623 else 624 { 625 /* There aren't any resources, so simply return NULL */ 626 DPRINT1("PciQueryRequirements returning NULL requirements list\n"); 627 *RequirementsList = NULL; 628 } 629 630 /* This call always succeeds (but maybe with no requirements) */ 631 return STATUS_SUCCESS; 632 } 633 634 /* 635 * 7. The IO/MEM/Busmaster decodes are disabled for the device. 636 * 8. The PCI bus driver sets the operating mode bits of the Programming 637 * Interface byte to switch the controller to native mode. 638 * 639 * Important: When the controller is set to native mode, it must quiet itself 640 * and must not decode I/O resources or generate interrupts until the operating 641 * system has enabled the ports in the PCI configuration header. 642 * The IO/MEM/BusMaster bits will be disabled before the mode change, but it 643 * is not possible to disable interrupts on the device. The device must not 644 * generate interrupts (either legacy or native mode) while the decodes are 645 * disabled in the command register. 646 * 647 * This operation is expected to be instantaneous and the operating system does 648 * not stall afterward. It is also expected that the interrupt pin register in 649 * the PCI Configuration space for this device is accurate. The operating system 650 * re-reads this data after previously ignoring it. 651 */ 652 BOOLEAN 653 NTAPI 654 PciConfigureIdeController(IN PPCI_PDO_EXTENSION PdoExtension, 655 IN PPCI_COMMON_HEADER PciData, 656 IN BOOLEAN Initial) 657 { 658 UCHAR MasterMode, SlaveMode, MasterFixed, SlaveFixed, ProgIf, NewProgIf; 659 BOOLEAN Switched; 660 USHORT Command; 661 662 /* Assume it won't work */ 663 Switched = FALSE; 664 665 /* Get master and slave current settings, and programmability flag */ 666 ProgIf = PciData->ProgIf; 667 MasterMode = (ProgIf & 1) == 1; 668 MasterFixed = (ProgIf & 2) == 0; 669 SlaveMode = (ProgIf & 4) == 4; 670 SlaveFixed = (ProgIf & 8) == 0; 671 672 /* 673 * [..] In order for Windows XP SP1 and Windows Server 2003 to switch an ATA 674 * ATA controller from compatible mode to native mode, the following must be 675 * true: 676 * 677 * - The controller must indicate in its programming interface that both channels 678 * can be switched to native mode. Windows XP SP1 and Windows Server 2003 do 679 * not support switching only one IDE channel to native mode. See the PCI IDE 680 * Controller Specification Revision 1.0 for details. 681 */ 682 if ((MasterMode != SlaveMode) || (MasterFixed != SlaveFixed)) 683 { 684 /* Windows does not support this configuration, fail */ 685 DPRINT1("PCI: Warning unsupported IDE controller configuration for VEN_%04x&DEV_%04x!", 686 PdoExtension->VendorId, 687 PdoExtension->DeviceId); 688 return Switched; 689 } 690 691 /* Check if the controller is already in native mode */ 692 if ((MasterMode) && (SlaveMode)) 693 { 694 /* Check if I/O decodes should be disabled */ 695 if ((Initial) || (PdoExtension->IoSpaceUnderNativeIdeControl)) 696 { 697 /* Read the current command */ 698 PciReadDeviceConfig(PdoExtension, 699 &Command, 700 FIELD_OFFSET(PCI_COMMON_HEADER, Command), 701 sizeof(USHORT)); 702 703 /* Disable I/O space decode */ 704 Command &= ~PCI_ENABLE_IO_SPACE; 705 706 /* Update new command in PCI IDE controller */ 707 PciWriteDeviceConfig(PdoExtension, 708 &Command, 709 FIELD_OFFSET(PCI_COMMON_HEADER, Command), 710 sizeof(USHORT)); 711 712 /* Save updated command value */ 713 PciData->Command = Command; 714 } 715 716 /* The controller is now in native mode */ 717 Switched = TRUE; 718 } 719 else if (!(MasterFixed) && 720 !(SlaveFixed) && 721 (PdoExtension->BIOSAllowsIDESwitchToNativeMode) && 722 !(PdoExtension->HackFlags & PCI_HACK_DISABLE_IDE_NATIVE_MODE)) 723 { 724 /* Turn off decodes */ 725 PciDecodeEnable(PdoExtension, FALSE, NULL); 726 727 /* Update the current command */ 728 PciReadDeviceConfig(PdoExtension, 729 &PciData->Command, 730 FIELD_OFFSET(PCI_COMMON_HEADER, Command), 731 sizeof(USHORT)); 732 733 /* Enable native mode */ 734 ProgIf = PciData->ProgIf | 5; 735 PciWriteDeviceConfig(PdoExtension, 736 &ProgIf, 737 FIELD_OFFSET(PCI_COMMON_HEADER, ProgIf), 738 sizeof(UCHAR)); 739 740 /* Verify the setting "stuck" */ 741 PciReadDeviceConfig(PdoExtension, 742 &NewProgIf, 743 FIELD_OFFSET(PCI_COMMON_HEADER, ProgIf), 744 sizeof(UCHAR)); 745 if (NewProgIf == ProgIf) 746 { 747 /* Update the header and PDO data with the new programming mode */ 748 PciData->ProgIf = ProgIf; 749 PdoExtension->ProgIf = NewProgIf; 750 751 /* Clear the first four BARs to reset current BAR settings */ 752 PciData->u.type0.BaseAddresses[0] = 0; 753 PciData->u.type0.BaseAddresses[1] = 0; 754 PciData->u.type0.BaseAddresses[2] = 0; 755 PciData->u.type0.BaseAddresses[3] = 0; 756 PciWriteDeviceConfig(PdoExtension, 757 PciData->u.type0.BaseAddresses, 758 FIELD_OFFSET(PCI_COMMON_HEADER, 759 u.type0.BaseAddresses), 760 4 * sizeof(ULONG)); 761 762 /* Re-read the BARs to have the latest data for native mode IDE */ 763 PciReadDeviceConfig(PdoExtension, 764 PciData->u.type0.BaseAddresses, 765 FIELD_OFFSET(PCI_COMMON_HEADER, 766 u.type0.BaseAddresses), 767 4 * sizeof(ULONG)); 768 769 /* Re-read the interrupt pin used for native mode IDE */ 770 PciReadDeviceConfig(PdoExtension, 771 &PciData->u.type0.InterruptPin, 772 FIELD_OFFSET(PCI_COMMON_HEADER, 773 u.type0.InterruptPin), 774 sizeof(UCHAR)); 775 776 /* The IDE Controller is now in native mode */ 777 Switched = TRUE; 778 } 779 else 780 { 781 /* Settings did not work, fail */ 782 DPRINT1("PCI: Warning failed switch to native mode for IDE controller VEN_%04x&DEV_%04x!", 783 PciData->VendorID, 784 PciData->DeviceID); 785 } 786 } 787 788 /* Return whether or not native mode was enabled on the IDE controller */ 789 return Switched; 790 } 791 792 VOID 793 NTAPI 794 PciApplyHacks(IN PPCI_FDO_EXTENSION DeviceExtension, 795 IN PPCI_COMMON_HEADER PciData, 796 IN PCI_SLOT_NUMBER SlotNumber, 797 IN ULONG OperationType, 798 PPCI_PDO_EXTENSION PdoExtension) 799 { 800 ULONG LegacyBaseAddress; 801 USHORT Command; 802 UCHAR RegValue; 803 804 UNREFERENCED_PARAMETER(SlotNumber); 805 806 /* Check what kind of hack operation this is */ 807 switch (OperationType) 808 { 809 /* 810 * This is mostly concerned with fixing up incorrect class data that can 811 * exist on certain PCI hardware before the 2.0 spec was ratified. 812 */ 813 case PCI_HACK_FIXUP_BEFORE_CONFIGURATION: 814 815 /* Note that the i82375 PCI/EISA and the i82378 PCI/ISA bridges that 816 * are present on certain DEC/NT Alpha machines are pre-PCI 2.0 devices 817 * and appear as non-classified, so their correct class/subclass data 818 * is written here instead. 819 */ 820 if ((PciData->VendorID == 0x8086) && 821 ((PciData->DeviceID == 0x482) || (PciData->DeviceID == 0x484))) 822 { 823 /* Note that 0x482 is the i82375 (EISA), 0x484 is the i82378 (ISA) */ 824 PciData->SubClass = PciData->DeviceID == 0x482 ? 825 PCI_SUBCLASS_BR_EISA : PCI_SUBCLASS_BR_ISA; 826 PciData->BaseClass = PCI_CLASS_BRIDGE_DEV; 827 828 /* 829 * Because the software is modifying the actual header data from 830 * the BIOS, this flag tells the driver to ignore failures when 831 * comparing the original BIOS data with the PCI data. 832 */ 833 if (PdoExtension) PdoExtension->ExpectedWritebackFailure = TRUE; 834 } 835 836 /* Note that in this case, an immediate return is issued */ 837 return; 838 839 /* 840 * This is concerned with setting up interrupts correctly for native IDE 841 * mode, but will also handle broken VGA decoding on older bridges as 842 * well as a PAE-specific hack for certain Compaq Hot-Plug Controllers. 843 */ 844 case PCI_HACK_FIXUP_AFTER_CONFIGURATION: 845 846 /* There should always be a PDO extension passed in */ 847 ASSERT(PdoExtension); 848 849 /* 850 * On the OPTi Viper-M IDE controller, Linux doesn't support IDE-DMA 851 * and FreeBSD bug reports indicate that the system crashes when the 852 * feature is enabled (so it's disabled on that OS as well). In the 853 * NT PCI Bus Driver, it seems Microsoft too, completely disables 854 * Native IDE functionality on this controller, so it would seem OPTi 855 * simply frelled up this controller. 856 */ 857 if ((PciData->VendorID == 0x1045) && (PciData->DeviceID != 0xC621)) 858 { 859 /* Disable native mode */ 860 PciData->ProgIf &= ~5; 861 PciData->u.type0.InterruptPin = 0; 862 863 /* 864 * Because the software is modifying the actual header data from 865 * the BIOS, this flag tells the driver to ignore failures when 866 * comparing the original BIOS data with the PCI data. 867 */ 868 PdoExtension->ExpectedWritebackFailure = TRUE; 869 } 870 else if ((PciData->BaseClass == PCI_CLASS_MASS_STORAGE_CTLR) && 871 (PciData->SubClass == PCI_SUBCLASS_MSC_IDE_CTLR)) 872 { 873 /* For other IDE controllers, start out in compatible mode */ 874 PdoExtension->BIOSAllowsIDESwitchToNativeMode = FALSE; 875 876 /* 877 * Registry must have enabled native mode (typically as a result 878 * of an INF file directive part of the IDE controller's driver) 879 * and the system must not be booted in Safe Mode. If that checks 880 * out, then evaluate the ACPI NATA method to see if the platform 881 * supports this. See the section "BIOS and Platform Prerequisites 882 * for Switching a Native-Mode-Capable Controller" in the Storage 883 * section of the Windows Driver Kit for more details: 884 * 885 * 5. For each ATA controller enumerated, the PCI bus driver checks 886 * the Programming Interface register of the IDE controller to 887 * see if it supports switching both channels to native mode. 888 * 6. The PCI bus driver checks whether the BIOS/platform supports 889 * switching the controller by checking the NATA method described 890 * earlier in this article. 891 * 892 * If an ATA controller does not indicate that it is native 893 * mode-capable, or if the BIOS NATA control method is missing 894 * or does not list that device, the PCI bus driver does not 895 * switch the controller and it is assigned legacy resources. 896 * 897 * If both the controller and the BIOS indicate that the controller 898 * can be switched, the process of switching the controller begins 899 * with the next step. 900 */ 901 if ((PciEnableNativeModeATA) && 902 !(InitSafeBootMode) && 903 (PciIsSlotPresentInParentMethod(PdoExtension, 'ATAN'))) 904 { 905 /* The platform supports it, remember that */ 906 PdoExtension->BIOSAllowsIDESwitchToNativeMode = TRUE; 907 908 /* 909 * Now switch the controller into native mode if both channels 910 * support native IDE mode. See "How Windows Switches an ATA 911 * Controller to Native Mode" in the Storage section of the 912 * Windows Driver Kit for more details. 913 */ 914 PdoExtension->IDEInNativeMode = 915 PciConfigureIdeController(PdoExtension, PciData, TRUE); 916 } 917 918 /* Is native mode enabled after all? */ 919 if ((PciData->ProgIf & 5) != 5) 920 { 921 /* Compatible mode, so force ISA-style IRQ14 and IRQ 15 */ 922 PciData->u.type0.InterruptPin = 0; 923 } 924 } 925 926 /* Is this a PCI device with legacy VGA card decodes on the root bus? */ 927 if ((PdoExtension->HackFlags & PCI_HACK_VIDEO_LEGACY_DECODE) && 928 (PCI_IS_ROOT_FDO(DeviceExtension)) && 929 !(DeviceExtension->BrokenVideoHackApplied)) 930 { 931 /* Tell the arbiter to apply a hack for these older devices */ 932 ario_ApplyBrokenVideoHack(DeviceExtension); 933 } 934 935 /* Is this a Compaq PCI Hotplug Controller (r17) on a PAE system ? */ 936 if ((PciData->VendorID == 0xE11) && 937 (PciData->DeviceID == 0xA0F7) && 938 (PciData->RevisionID == 17) && 939 (ExIsProcessorFeaturePresent(PF_PAE_ENABLED))) 940 { 941 /* Turn off the decodes immediately */ 942 PciData->Command &= ~(PCI_ENABLE_IO_SPACE | 943 PCI_ENABLE_MEMORY_SPACE | 944 PCI_ENABLE_BUS_MASTER); 945 PciWriteDeviceConfig(PdoExtension, 946 &PciData->Command, 947 FIELD_OFFSET(PCI_COMMON_HEADER, Command), 948 sizeof(USHORT)); 949 950 /* Do not EVER turn them on again, this will blow up the system */ 951 PdoExtension->CommandEnables &= ~(PCI_ENABLE_IO_SPACE | 952 PCI_ENABLE_MEMORY_SPACE | 953 PCI_ENABLE_BUS_MASTER); 954 PdoExtension->HackFlags |= PCI_HACK_PRESERVE_COMMAND; 955 } 956 break; 957 958 /* 959 * This is called whenever resources are changed and hardware needs to be 960 * updated. It is concerned with two highly specific erratas on an IBM 961 * hot-plug docking bridge used on the Thinkpad 600 Series and on Intel's 962 * ICH PCI Bridges. 963 */ 964 case PCI_HACK_FIXUP_BEFORE_UPDATE: 965 966 /* There should always be a PDO extension passed in */ 967 ASSERT(PdoExtension); 968 969 /* Is this an IBM 20H2999 PCI Docking Bridge, used on Thinkpads? */ 970 if ((PdoExtension->VendorId == 0x1014) && 971 (PdoExtension->DeviceId == 0x95)) 972 { 973 /* Read the current command */ 974 PciReadDeviceConfig(PdoExtension, 975 &Command, 976 FIELD_OFFSET(PCI_COMMON_HEADER, Command), 977 sizeof(USHORT)); 978 979 /* Turn off the decodes */ 980 PciDecodeEnable(PdoExtension, FALSE, &Command); 981 982 /* Apply the required IBM workaround */ 983 PciReadDeviceConfig(PdoExtension, &RegValue, 0xE0, sizeof(UCHAR)); 984 RegValue &= ~2; 985 RegValue |= 1; 986 PciWriteDeviceConfig(PdoExtension, &RegValue, 0xE0, sizeof(UCHAR)); 987 988 /* Restore the command to its original value */ 989 PciWriteDeviceConfig(PdoExtension, 990 &Command, 991 FIELD_OFFSET(PCI_COMMON_HEADER, Command), 992 sizeof(USHORT)); 993 994 } 995 996 /* 997 * Check for Intel ICH PCI-to-PCI (i82801) bridges (used on the i810, 998 * i820, i840, i845 Chipsets) that have subtractive decode enabled, 999 * and whose hack flags do not specify that this support is broken. 1000 */ 1001 if ((PdoExtension->HeaderType == PCI_BRIDGE_TYPE) && 1002 (PdoExtension->Dependent.type1.SubtractiveDecode) && 1003 ((PdoExtension->VendorId == 0x8086) && 1004 ((PdoExtension->DeviceId == 0x2418) || 1005 (PdoExtension->DeviceId == 0x2428) || 1006 (PdoExtension->DeviceId == 0x244E) || 1007 (PdoExtension->DeviceId == 0x2448))) && 1008 !(PdoExtension->HackFlags & PCI_HACK_BROKEN_SUBTRACTIVE_DECODE)) 1009 { 1010 /* 1011 * The positive decode window shouldn't be used, these values are 1012 * normally all read-only or initialized to 0 by the BIOS, but 1013 * it appears Intel doesn't do this, so the PCI Bus Driver will 1014 * do it in software instead. Note that this is used to prevent 1015 * certain non-compliant PCI devices from breaking down due to the 1016 * fact that these ICH bridges have a known "quirk" (which Intel 1017 * documents as a known "erratum", although it's not not really 1018 * an ICH bug since the PCI specification does allow for it) in 1019 * that they will sometimes send non-zero addresses during special 1020 * cycles (ie: non-zero data during the address phase). These 1021 * broken PCI cards will mistakenly attempt to claim the special 1022 * cycle and corrupt their I/O and RAM ranges. Again, in Intel's 1023 * defense, the PCI specification only requires stable data, not 1024 * necessarily zero data, during the address phase. 1025 */ 1026 PciData->u.type1.MemoryBase = 0xFFFF; 1027 PciData->u.type1.PrefetchBase = 0xFFFF; 1028 PciData->u.type1.IOBase = 0xFF; 1029 PciData->u.type1.IOLimit = 0; 1030 PciData->u.type1.MemoryLimit = 0; 1031 PciData->u.type1.PrefetchLimit = 0; 1032 PciData->u.type1.PrefetchBaseUpper32 = 0; 1033 PciData->u.type1.PrefetchLimitUpper32 = 0; 1034 PciData->u.type1.IOBaseUpper16 = 0; 1035 PciData->u.type1.IOLimitUpper16 = 0; 1036 } 1037 break; 1038 1039 default: 1040 return; 1041 } 1042 1043 /* Finally, also check if this is this a CardBUS device? */ 1044 if (PCI_CONFIGURATION_TYPE(PciData) == PCI_CARDBUS_BRIDGE_TYPE) 1045 { 1046 /* 1047 * At offset 44h the LegacyBaseAddress is stored, which is cleared by 1048 * ACPI-aware versions of Windows, to disable legacy-mode I/O access to 1049 * CardBus controllers. For more information, see "Supporting CardBus 1050 * Controllers under ACPI" in the "CardBus Controllers and Windows" 1051 * Whitepaper on WHDC. 1052 */ 1053 LegacyBaseAddress = 0; 1054 PciWriteDeviceConfig(PdoExtension, 1055 &LegacyBaseAddress, 1056 sizeof(PCI_COMMON_HEADER) + sizeof(ULONG), 1057 sizeof(ULONG)); 1058 } 1059 } 1060 1061 BOOLEAN 1062 NTAPI 1063 PcipIsSameDevice(IN PPCI_PDO_EXTENSION DeviceExtension, 1064 IN PPCI_COMMON_HEADER PciData) 1065 { 1066 BOOLEAN IdMatch, RevMatch, SubsysMatch; 1067 ULONGLONG HackFlags = DeviceExtension->HackFlags; 1068 1069 /* Check if the IDs match */ 1070 IdMatch = (PciData->VendorID == DeviceExtension->VendorId) && 1071 (PciData->DeviceID == DeviceExtension->DeviceId); 1072 if (!IdMatch) return FALSE; 1073 1074 /* If the device has a valid revision, check if it matches */ 1075 RevMatch = (HackFlags & PCI_HACK_NO_REVISION_AFTER_D3) || 1076 (PciData->RevisionID == DeviceExtension->RevisionId); 1077 if (!RevMatch) return FALSE; 1078 1079 /* For multifunction devices, this is enough to assume they're the same */ 1080 if (PCI_MULTIFUNCTION_DEVICE(PciData)) return TRUE; 1081 1082 /* For bridge devices, there's also nothing else that can be checked */ 1083 if (DeviceExtension->BaseClass == PCI_CLASS_BRIDGE_DEV) return TRUE; 1084 1085 /* Devices, on the other hand, have subsystem data that can be compared */ 1086 SubsysMatch = (HackFlags & (PCI_HACK_NO_SUBSYSTEM | 1087 PCI_HACK_NO_SUBSYSTEM_AFTER_D3)) || 1088 ((DeviceExtension->SubsystemVendorId == 1089 PciData->u.type0.SubVendorID) && 1090 (DeviceExtension->SubsystemId == 1091 PciData->u.type0.SubSystemID)); 1092 return SubsysMatch; 1093 } 1094 1095 BOOLEAN 1096 NTAPI 1097 PciSkipThisFunction(IN PPCI_COMMON_HEADER PciData, 1098 IN PCI_SLOT_NUMBER Slot, 1099 IN UCHAR OperationType, 1100 IN ULONGLONG HackFlags) 1101 { 1102 do 1103 { 1104 /* Check if this is device enumeration */ 1105 if (OperationType == PCI_SKIP_DEVICE_ENUMERATION) 1106 { 1107 /* Check if there's a hackflag saying not to enumerate this device */ 1108 if (HackFlags & PCI_HACK_NO_ENUM_AT_ALL) break; 1109 1110 /* Check if this is the high end of a double decker device */ 1111 if ((HackFlags & PCI_HACK_DOUBLE_DECKER) && 1112 (Slot.u.bits.DeviceNumber >= 16)) 1113 { 1114 /* It belongs to the same device, so skip it */ 1115 DPRINT1(" Device (Ven %04x Dev %04x (d=0x%x, f=0x%x)) is a ghost.\n", 1116 PciData->VendorID, 1117 PciData->DeviceID, 1118 Slot.u.bits.DeviceNumber, 1119 Slot.u.bits.FunctionNumber); 1120 break; 1121 } 1122 } 1123 else if (OperationType == PCI_SKIP_RESOURCE_ENUMERATION) 1124 { 1125 /* Resource enumeration, check for a hackflag saying not to do it */ 1126 if (HackFlags & PCI_HACK_ENUM_NO_RESOURCE) break; 1127 } 1128 else 1129 { 1130 /* Logic error in the driver */ 1131 ASSERTMSG("PCI Skip Function - Operation type unknown.\n", FALSE); 1132 } 1133 1134 /* Check for legacy bridges during resource enumeration */ 1135 if ((PciData->BaseClass == PCI_CLASS_BRIDGE_DEV) && 1136 (PciData->SubClass <= PCI_SUBCLASS_BR_MCA) && 1137 (OperationType == PCI_SKIP_RESOURCE_ENUMERATION)) 1138 { 1139 /* Their resources are not enumerated, only PCI and Cardbus/PCMCIA */ 1140 break; 1141 } 1142 else if (PciData->BaseClass == PCI_CLASS_NOT_DEFINED) 1143 { 1144 /* Undefined base class (usually a PCI BIOS/ROM bug) */ 1145 DPRINT1(" Vendor %04x, Device %04x has class code of PCI_CLASS_NOT_DEFINED\n", 1146 PciData->VendorID, 1147 PciData->DeviceID); 1148 1149 /* 1150 * The Alder has an Intel Extended Express System Support Controller 1151 * which presents apparently spurious BARs. When the PCI resource 1152 * code tries to reassign these BARs, the second IO-APIC gets 1153 * disabled (with disastrous consequences). The first BAR is the 1154 * actual IO-APIC, the remaining five bars seem to be spurious 1155 * resources, so ignore this device completely. 1156 */ 1157 if ((PciData->VendorID == 0x8086) && (PciData->DeviceID == 8)) break; 1158 } 1159 1160 /* Other normal PCI cards and bridges are enumerated */ 1161 if (PCI_CONFIGURATION_TYPE(PciData) <= PCI_CARDBUS_BRIDGE_TYPE) return FALSE; 1162 } while (FALSE); 1163 1164 /* Hit one of the known bugs/hackflags, or this is a new kind of PCI unit */ 1165 DPRINT1(" Device skipped (not enumerated).\n"); 1166 return TRUE; 1167 } 1168 1169 VOID 1170 NTAPI 1171 PciGetEnhancedCapabilities(IN PPCI_PDO_EXTENSION PdoExtension, 1172 IN PPCI_COMMON_HEADER PciData) 1173 { 1174 ULONG HeaderType, CapPtr, TargetAgpCapabilityId; 1175 DEVICE_POWER_STATE WakeLevel; 1176 PCI_CAPABILITIES_HEADER AgpCapability; 1177 PCI_PM_CAPABILITY PowerCapabilities; 1178 PAGED_CODE(); 1179 1180 /* Assume no known wake level */ 1181 PdoExtension->PowerState.DeviceWakeLevel = PowerDeviceUnspecified; 1182 1183 /* Make sure the device has capabilities */ 1184 if (!(PciData->Status & PCI_STATUS_CAPABILITIES_LIST)) 1185 { 1186 /* If it doesn't, there will be no power management */ 1187 PdoExtension->CapabilitiesPtr = 0; 1188 PdoExtension->HackFlags |= PCI_HACK_NO_PM_CAPS; 1189 } 1190 else 1191 { 1192 /* There's capabilities, need to figure out where to get the offset */ 1193 HeaderType = PCI_CONFIGURATION_TYPE(PciData); 1194 if (HeaderType == PCI_CARDBUS_BRIDGE_TYPE) 1195 { 1196 /* Use the bridge's header */ 1197 CapPtr = PciData->u.type2.CapabilitiesPtr; 1198 } 1199 else 1200 { 1201 /* Use the device header */ 1202 ASSERT(HeaderType <= PCI_CARDBUS_BRIDGE_TYPE); 1203 CapPtr = PciData->u.type0.CapabilitiesPtr; 1204 } 1205 1206 /* Skip garbage capabilities pointer */ 1207 if (((CapPtr & 0x3) != 0) || (CapPtr < PCI_COMMON_HDR_LENGTH)) 1208 { 1209 /* Report no extended capabilities */ 1210 PdoExtension->CapabilitiesPtr = 0; 1211 PdoExtension->HackFlags |= PCI_HACK_NO_PM_CAPS; 1212 } 1213 else 1214 { 1215 DPRINT1("Device has capabilities at: %lx\n", CapPtr); 1216 PdoExtension->CapabilitiesPtr = CapPtr; 1217 1218 /* Check for PCI-to-PCI Bridges and AGP bridges */ 1219 if ((PdoExtension->BaseClass == PCI_CLASS_BRIDGE_DEV) && 1220 ((PdoExtension->SubClass == PCI_SUBCLASS_BR_HOST) || 1221 (PdoExtension->SubClass == PCI_SUBCLASS_BR_PCI_TO_PCI))) 1222 { 1223 /* Query either the raw AGP capabilitity, or the Target AGP one */ 1224 TargetAgpCapabilityId = (PdoExtension->SubClass == 1225 PCI_SUBCLASS_BR_PCI_TO_PCI) ? 1226 PCI_CAPABILITY_ID_AGP_TARGET : 1227 PCI_CAPABILITY_ID_AGP; 1228 if (PciReadDeviceCapability(PdoExtension, 1229 PdoExtension->CapabilitiesPtr, 1230 TargetAgpCapabilityId, 1231 &AgpCapability, 1232 sizeof(PCI_CAPABILITIES_HEADER))) 1233 { 1234 /* AGP target ID was found, store it */ 1235 DPRINT1("AGP ID: %lx\n", TargetAgpCapabilityId); 1236 PdoExtension->TargetAgpCapabilityId = TargetAgpCapabilityId; 1237 } 1238 } 1239 1240 /* Check for devices that are known not to have proper power management */ 1241 if (!(PdoExtension->HackFlags & PCI_HACK_NO_PM_CAPS)) 1242 { 1243 /* Query if this device supports power management */ 1244 if (!PciReadDeviceCapability(PdoExtension, 1245 PdoExtension->CapabilitiesPtr, 1246 PCI_CAPABILITY_ID_POWER_MANAGEMENT, 1247 &PowerCapabilities.Header, 1248 sizeof(PCI_PM_CAPABILITY))) 1249 { 1250 /* No power management, so act as if it had the hackflag set */ 1251 DPRINT1("No PM caps, disabling PM\n"); 1252 PdoExtension->HackFlags |= PCI_HACK_NO_PM_CAPS; 1253 } 1254 else 1255 { 1256 /* Otherwise, pick the highest wake level that is supported */ 1257 WakeLevel = PowerDeviceUnspecified; 1258 if (PowerCapabilities.PMC.Capabilities.Support.PMED0) 1259 WakeLevel = PowerDeviceD0; 1260 if (PowerCapabilities.PMC.Capabilities.Support.PMED1) 1261 WakeLevel = PowerDeviceD1; 1262 if (PowerCapabilities.PMC.Capabilities.Support.PMED2) 1263 WakeLevel = PowerDeviceD2; 1264 if (PowerCapabilities.PMC.Capabilities.Support.PMED3Hot) 1265 WakeLevel = PowerDeviceD3; 1266 if (PowerCapabilities.PMC.Capabilities.Support.PMED3Cold) 1267 WakeLevel = PowerDeviceD3; 1268 PdoExtension->PowerState.DeviceWakeLevel = WakeLevel; 1269 1270 /* Convert the PCI power state to the NT power state */ 1271 PdoExtension->PowerState.CurrentDeviceState = 1272 PowerCapabilities.PMCSR.ControlStatus.PowerState + 1; 1273 1274 /* Save all the power capabilities */ 1275 PdoExtension->PowerCapabilities = PowerCapabilities.PMC.Capabilities; 1276 DPRINT1("PM Caps Found! Wake Level: %d Power State: %d\n", 1277 WakeLevel, PdoExtension->PowerState.CurrentDeviceState); 1278 } 1279 } 1280 } 1281 } 1282 1283 /* At the very end of all this, does this device not have power management? */ 1284 if (PdoExtension->HackFlags & PCI_HACK_NO_PM_CAPS) 1285 { 1286 /* Then guess the current state based on whether the decodes are on */ 1287 PdoExtension->PowerState.CurrentDeviceState = 1288 PciData->Command & (PCI_ENABLE_IO_SPACE | 1289 PCI_ENABLE_MEMORY_SPACE | 1290 PCI_ENABLE_BUS_MASTER) ? 1291 PowerDeviceD0: PowerDeviceD3; 1292 DPRINT1("PM is off, so assumed device is: %d based on enables\n", 1293 PdoExtension->PowerState.CurrentDeviceState); 1294 } 1295 } 1296 1297 VOID 1298 NTAPI 1299 PciWriteLimitsAndRestoreCurrent(IN PVOID Reserved, 1300 IN PVOID Context2) 1301 { 1302 PPCI_CONFIGURATOR_CONTEXT Context = Context2; 1303 PPCI_COMMON_HEADER PciData, Current; 1304 PPCI_PDO_EXTENSION PdoExtension; 1305 1306 UNREFERENCED_PARAMETER(Reserved); 1307 1308 /* Grab all parameters from the context */ 1309 PdoExtension = Context->PdoExtension; 1310 Current = Context->Current; 1311 PciData = Context->PciData; 1312 1313 /* Write the limit discovery header */ 1314 PciWriteDeviceConfig(PdoExtension, PciData, 0, PCI_COMMON_HDR_LENGTH); 1315 1316 /* Now read what the device indicated the limits are */ 1317 PciReadDeviceConfig(PdoExtension, PciData, 0, PCI_COMMON_HDR_LENGTH); 1318 1319 /* Then write back the original configuration header */ 1320 PciWriteDeviceConfig(PdoExtension, Current, 0, PCI_COMMON_HDR_LENGTH); 1321 1322 /* Copy back the original command that was saved in the context */ 1323 Current->Command = Context->Command; 1324 if (Context->Command) 1325 { 1326 /* Program it back into the device */ 1327 PciWriteDeviceConfig(PdoExtension, 1328 &Context->Command, 1329 FIELD_OFFSET(PCI_COMMON_HEADER, Command), 1330 sizeof(USHORT)); 1331 } 1332 1333 /* Copy back the original status that was saved as well */ 1334 Current->Status = Context->Status; 1335 1336 /* Call the configurator to restore any other data that might've changed */ 1337 Context->Configurator->RestoreCurrent(Context); 1338 } 1339 1340 NTSTATUS 1341 NTAPI 1342 PcipGetFunctionLimits(IN PPCI_CONFIGURATOR_CONTEXT Context) 1343 { 1344 PPCI_CONFIGURATOR Configurator; 1345 PPCI_COMMON_HEADER PciData, Current; 1346 PPCI_PDO_EXTENSION PdoExtension; 1347 PCI_IPI_CONTEXT IpiContext; 1348 PIO_RESOURCE_DESCRIPTOR IoDescriptor; 1349 ULONG Offset; 1350 PAGED_CODE(); 1351 1352 /* Grab all parameters from the context */ 1353 PdoExtension = Context->PdoExtension; 1354 Current = Context->Current; 1355 PciData = Context->PciData; 1356 1357 /* Save the current PCI Command and Status word */ 1358 Context->Status = Current->Status; 1359 Context->Command = Current->Command; 1360 1361 /* Now that they're saved, clear the status, and disable all decodes */ 1362 Current->Status = 0; 1363 Current->Command &= ~(PCI_ENABLE_IO_SPACE | 1364 PCI_ENABLE_MEMORY_SPACE | 1365 PCI_ENABLE_BUS_MASTER); 1366 1367 /* Make a copy of the current PCI configuration header (with decodes off) */ 1368 RtlCopyMemory(PciData, Current, PCI_COMMON_HDR_LENGTH); 1369 1370 /* Locate the correct resource configurator for this type of device */ 1371 Configurator = &PciConfigurators[PdoExtension->HeaderType]; 1372 Context->Configurator = Configurator; 1373 1374 /* Initialize it, which will typically setup the BARs for limit discovery */ 1375 Configurator->Initialize(Context); 1376 1377 /* Check for critical devices and PCI Debugging devices */ 1378 if ((PdoExtension->HackFlags & PCI_HACK_CRITICAL_DEVICE) || 1379 (PdoExtension->OnDebugPath)) 1380 { 1381 /* Specifically check for a PCI Debugging device */ 1382 if (PdoExtension->OnDebugPath) 1383 { 1384 /* Was it enabled for bus mastering? */ 1385 if (Context->Command & PCI_ENABLE_BUS_MASTER) 1386 { 1387 /* This decode needs to be re-enabled so debugging can work */ 1388 PciData->Command |= PCI_ENABLE_BUS_MASTER; 1389 Current->Command |= PCI_ENABLE_BUS_MASTER; 1390 } 1391 1392 /* Disable the debugger while the discovery is happening */ 1393 KdDisableDebugger(); 1394 } 1395 1396 /* For these devices, an IPI must be sent to force high-IRQL discovery */ 1397 IpiContext.Barrier = 1; 1398 IpiContext.RunCount = 1; 1399 IpiContext.DeviceExtension = PdoExtension; 1400 IpiContext.Function = PciWriteLimitsAndRestoreCurrent; 1401 IpiContext.Context = Context; 1402 KeIpiGenericCall(PciExecuteCriticalSystemRoutine, (ULONG_PTR)&IpiContext); 1403 1404 /* Re-enable the debugger if this was a PCI Debugging Device */ 1405 if (PdoExtension->OnDebugPath) KdEnableDebugger(); 1406 } 1407 else 1408 { 1409 /* Otherwise, it's safe to do this in-line at low IRQL */ 1410 PciWriteLimitsAndRestoreCurrent(PdoExtension, Context); 1411 } 1412 1413 /* 1414 * Check if it's valid to compare the headers to see if limit discovery mode 1415 * has properly exited (the expected case is that the PCI header would now 1416 * be equal to what it was before). In some cases, it is known that this will 1417 * fail, because during PciApplyHacks (among other places), software hacks 1418 * had to be applied to the header, which the hardware-side will not see, and 1419 * thus the headers would appear "different". 1420 */ 1421 if (!PdoExtension->ExpectedWritebackFailure) 1422 { 1423 /* Read the current PCI header now, after discovery has completed */ 1424 PciReadDeviceConfig(PdoExtension, PciData + 1, 0, PCI_COMMON_HDR_LENGTH); 1425 1426 /* Check if the current header at entry, is equal to the header now */ 1427 Offset = RtlCompareMemory(PciData + 1, Current, PCI_COMMON_HDR_LENGTH); 1428 if (Offset != PCI_COMMON_HDR_LENGTH) 1429 { 1430 /* It's not, which means configuration somehow changed, dump this */ 1431 DPRINT1("PCI - CFG space write verify failed at offset 0x%x\n", Offset); 1432 PciDebugDumpCommonConfig(PciData + 1); 1433 DPRINT1("----------\n"); 1434 PciDebugDumpCommonConfig(Current); 1435 } 1436 } 1437 1438 /* This PDO should not already have resources, since this is only done once */ 1439 ASSERT(PdoExtension->Resources == NULL); 1440 1441 /* Allocate the structure that will hold the discovered resources and limits */ 1442 PdoExtension->Resources = ExAllocatePoolWithTag(NonPagedPool, 1443 sizeof(PCI_FUNCTION_RESOURCES), 1444 'BicP'); 1445 if (!PdoExtension->Resources) return STATUS_INSUFFICIENT_RESOURCES; 1446 1447 /* Clear it out for now */ 1448 RtlZeroMemory(PdoExtension->Resources, sizeof(PCI_FUNCTION_RESOURCES)); 1449 1450 /* Now call the configurator, which will first store the limits... */ 1451 Configurator->SaveLimits(Context); 1452 1453 /* ...and then store the current resources being used */ 1454 Configurator->SaveCurrentSettings(Context); 1455 1456 /* Loop all the limit descriptors backwards */ 1457 IoDescriptor = &PdoExtension->Resources->Limit[PCI_TYPE0_ADDRESSES + 1]; 1458 while (TRUE) 1459 { 1460 /* Keep going until a non-null descriptor is found */ 1461 IoDescriptor--; 1462 if (IoDescriptor->Type != CmResourceTypeNull) break; 1463 1464 /* This is a null descriptor, is it the last one? */ 1465 if (IoDescriptor == &PdoExtension->Resources->Limit[PCI_TYPE0_ADDRESSES + 1]) 1466 { 1467 /* This means the descriptor is NULL, which means discovery failed */ 1468 DPRINT1("PCI Resources fail!\n"); 1469 1470 /* No resources will be assigned for the device */ 1471 ExFreePoolWithTag(PdoExtension->Resources, 0); 1472 PdoExtension->Resources = NULL; 1473 break; 1474 } 1475 } 1476 1477 /* Return success here, even if the device has no assigned resources */ 1478 return STATUS_SUCCESS; 1479 } 1480 1481 NTSTATUS 1482 NTAPI 1483 PciGetFunctionLimits(IN PPCI_PDO_EXTENSION PdoExtension, 1484 IN PPCI_COMMON_HEADER Current, 1485 IN ULONGLONG HackFlags) 1486 { 1487 NTSTATUS Status; 1488 PPCI_COMMON_HEADER PciData; 1489 PCI_CONFIGURATOR_CONTEXT Context; 1490 PAGED_CODE(); 1491 1492 /* Do the hackflags indicate this device should be skipped? */ 1493 if (PciSkipThisFunction(Current, 1494 PdoExtension->Slot, 1495 PCI_SKIP_RESOURCE_ENUMERATION, 1496 HackFlags)) 1497 { 1498 /* Do not process its resources */ 1499 return STATUS_SUCCESS; 1500 } 1501 1502 /* Allocate a buffer to hold two PCI configuration headers */ 1503 PciData = ExAllocatePoolWithTag(0, 2 * PCI_COMMON_HDR_LENGTH, 'BicP'); 1504 if (!PciData) return STATUS_INSUFFICIENT_RESOURCES; 1505 1506 /* Set up the context for the resource enumeration, and do it */ 1507 Context.Current = Current; 1508 Context.PciData = PciData; 1509 Context.PdoExtension = PdoExtension; 1510 Status = PcipGetFunctionLimits(&Context); 1511 1512 /* Enumeration is completed, free the PCI headers and return the status */ 1513 ExFreePoolWithTag(PciData, 0); 1514 return Status; 1515 } 1516 1517 VOID 1518 NTAPI 1519 PciProcessBus(IN PPCI_FDO_EXTENSION DeviceExtension) 1520 { 1521 PPCI_PDO_EXTENSION PdoExtension; 1522 PDEVICE_OBJECT PhysicalDeviceObject; 1523 PAGED_CODE(); 1524 1525 /* Get the PDO Extension */ 1526 PhysicalDeviceObject = DeviceExtension->PhysicalDeviceObject; 1527 PdoExtension = (PPCI_PDO_EXTENSION)PhysicalDeviceObject->DeviceExtension; 1528 1529 /* Cheeck if this is the root bus */ 1530 if (!PCI_IS_ROOT_FDO(DeviceExtension)) 1531 { 1532 /* Not really handling this year */ 1533 UNIMPLEMENTED_DBGBREAK(); 1534 1535 /* Check for PCI bridges with the ISA bit set, or required */ 1536 if ((PdoExtension) && 1537 (PciClassifyDeviceType(PdoExtension) == PciTypePciBridge) && 1538 ((PdoExtension->Dependent.type1.IsaBitRequired) || 1539 (PdoExtension->Dependent.type1.IsaBitSet))) 1540 { 1541 /* We'll need to do some legacy support */ 1542 UNIMPLEMENTED_DBGBREAK(); 1543 } 1544 } 1545 else 1546 { 1547 /* Scan all of the root bus' children bridges */ 1548 for (PdoExtension = DeviceExtension->ChildBridgePdoList; 1549 PdoExtension; 1550 PdoExtension = PdoExtension->NextBridge) 1551 { 1552 /* Find any that have the VGA decode bit on */ 1553 if (PdoExtension->Dependent.type1.VgaBitSet) 1554 { 1555 /* Again, some more legacy support we'll have to do */ 1556 UNIMPLEMENTED_DBGBREAK(); 1557 } 1558 } 1559 } 1560 1561 /* Check for ACPI systems where the OS assigns bus numbers */ 1562 if (PciAssignBusNumbers) 1563 { 1564 /* Not yet supported */ 1565 UNIMPLEMENTED_DBGBREAK(); 1566 } 1567 } 1568 1569 NTSTATUS 1570 NTAPI 1571 PciScanBus(IN PPCI_FDO_EXTENSION DeviceExtension) 1572 { 1573 ULONG MaxDevice = PCI_MAX_DEVICES; 1574 BOOLEAN ProcessFlag = FALSE; 1575 ULONG i, j, k, Size; 1576 USHORT CapOffset, TempOffset; 1577 LONGLONG HackFlags; 1578 PDEVICE_OBJECT DeviceObject; 1579 UCHAR Buffer[PCI_COMMON_HDR_LENGTH]; 1580 UCHAR BiosBuffer[PCI_COMMON_HDR_LENGTH]; 1581 PPCI_COMMON_HEADER PciData = (PVOID)Buffer; 1582 PPCI_COMMON_HEADER BiosData = (PVOID)BiosBuffer; 1583 PCI_SLOT_NUMBER PciSlot; 1584 PCHAR Name; 1585 NTSTATUS Status; 1586 PPCI_PDO_EXTENSION PdoExtension, NewExtension; 1587 PPCI_PDO_EXTENSION* BridgeExtension; 1588 PWCHAR DescriptionText; 1589 USHORT SubVendorId, SubSystemId; 1590 PCI_CAPABILITIES_HEADER CapHeader, PcixCapHeader; 1591 UCHAR SecondaryBus; 1592 DPRINT1("PCI Scan Bus: FDO Extension @ 0x%p, Base Bus = 0x%x\n", 1593 DeviceExtension, DeviceExtension->BaseBus); 1594 1595 /* Is this the root FDO? */ 1596 if (!PCI_IS_ROOT_FDO(DeviceExtension)) 1597 { 1598 /* Get the PDO for the child bus */ 1599 PdoExtension = DeviceExtension->PhysicalDeviceObject->DeviceExtension; 1600 ASSERT_PDO(PdoExtension); 1601 1602 /* Check for hack which only allows bus to have one child device */ 1603 if (PdoExtension->HackFlags & PCI_HACK_ONE_CHILD) MaxDevice = 1; 1604 1605 /* Check if the secondary bus number has changed */ 1606 PciReadDeviceConfig(PdoExtension, 1607 &SecondaryBus, 1608 FIELD_OFFSET(PCI_COMMON_HEADER, u.type1.SecondaryBus), 1609 sizeof(UCHAR)); 1610 if (SecondaryBus != PdoExtension->Dependent.type1.SecondaryBus) 1611 { 1612 UNIMPLEMENTED_DBGBREAK("PCI: Bus numbers have been changed! Restoring originals.\n"); 1613 } 1614 } 1615 1616 /* Loop every device on the bus */ 1617 PciSlot.u.bits.Reserved = 0; 1618 i = DeviceExtension->BaseBus; 1619 for (j = 0; j < MaxDevice; j++) 1620 { 1621 /* Loop every function of each device */ 1622 PciSlot.u.bits.DeviceNumber = j; 1623 for (k = 0; k < PCI_MAX_FUNCTION; k++) 1624 { 1625 /* Build the final slot structure */ 1626 PciSlot.u.bits.FunctionNumber = k; 1627 1628 /* Read the vendor for this slot */ 1629 PciReadSlotConfig(DeviceExtension, 1630 PciSlot, 1631 PciData, 1632 0, 1633 sizeof(USHORT)); 1634 1635 /* Skip invalid device */ 1636 if (PciData->VendorID == PCI_INVALID_VENDORID) continue; 1637 1638 /* Now read the whole header */ 1639 PciReadSlotConfig(DeviceExtension, 1640 PciSlot, 1641 &PciData->DeviceID, 1642 sizeof(USHORT), 1643 PCI_COMMON_HDR_LENGTH - sizeof(USHORT)); 1644 1645 /* Apply any hacks before even analyzing the configuration header */ 1646 PciApplyHacks(DeviceExtension, 1647 PciData, 1648 PciSlot, 1649 PCI_HACK_FIXUP_BEFORE_CONFIGURATION, 1650 NULL); 1651 1652 /* Dump device that was found */ 1653 DPRINT1("Scan Found Device 0x%x (b=0x%x, d=0x%x, f=0x%x)\n", 1654 PciSlot.u.AsULONG, 1655 i, 1656 j, 1657 k); 1658 1659 /* Dump the device's header */ 1660 PciDebugDumpCommonConfig(PciData); 1661 1662 /* Find description for this device for the debugger's sake */ 1663 DescriptionText = PciGetDeviceDescriptionMessage(PciData->BaseClass, 1664 PciData->SubClass); 1665 DPRINT1("Device Description \"%S\".\n", 1666 DescriptionText ? DescriptionText : L"(NULL)"); 1667 if (DescriptionText) ExFreePoolWithTag(DescriptionText, 0); 1668 1669 /* Check if there is an ACPI Watchdog Table */ 1670 if (WdTable) 1671 { 1672 /* Check if this PCI device is the ACPI Watchdog Device... */ 1673 UNIMPLEMENTED_DBGBREAK(); 1674 } 1675 1676 /* Check for non-simple devices */ 1677 if ((PCI_MULTIFUNCTION_DEVICE(PciData)) || 1678 (PciData->BaseClass == PCI_CLASS_BRIDGE_DEV)) 1679 { 1680 /* No subsystem data defined for these kinds of bridges */ 1681 SubVendorId = 0; 1682 SubSystemId = 0; 1683 } 1684 else 1685 { 1686 /* Read the subsystem information from the PCI header */ 1687 SubVendorId = PciData->u.type0.SubVendorID; 1688 SubSystemId = PciData->u.type0.SubSystemID; 1689 } 1690 1691 /* Get any hack flags for this device */ 1692 HackFlags = PciGetHackFlags(PciData->VendorID, 1693 PciData->DeviceID, 1694 SubVendorId, 1695 SubSystemId, 1696 PciData->RevisionID); 1697 1698 /* Check if this device is considered critical by the OS */ 1699 if (PciIsCriticalDeviceClass(PciData->BaseClass, PciData->SubClass)) 1700 { 1701 /* Check if normally the decodes would be disabled */ 1702 if (!(HackFlags & PCI_HACK_DONT_DISABLE_DECODES)) 1703 { 1704 /* Because this device is critical, don't disable them */ 1705 DPRINT1("Not allowing PM Because device is critical\n"); 1706 HackFlags |= PCI_HACK_CRITICAL_DEVICE; 1707 } 1708 } 1709 1710 /* PCI bridges with a VGA card are also considered critical */ 1711 if ((PciData->BaseClass == PCI_CLASS_BRIDGE_DEV) && 1712 (PciData->SubClass == PCI_SUBCLASS_BR_PCI_TO_PCI) && 1713 (PciData->u.type1.BridgeControl & PCI_ENABLE_BRIDGE_VGA) && 1714 !(HackFlags & PCI_HACK_DONT_DISABLE_DECODES)) 1715 { 1716 /* Do not disable their decodes either */ 1717 DPRINT1("Not allowing PM because device is VGA\n"); 1718 HackFlags |= PCI_HACK_CRITICAL_DEVICE; 1719 } 1720 1721 /* Check if the device should be skipped for whatever reason */ 1722 if (PciSkipThisFunction(PciData, 1723 PciSlot, 1724 PCI_SKIP_DEVICE_ENUMERATION, 1725 HackFlags)) 1726 { 1727 /* Skip this device */ 1728 continue; 1729 } 1730 1731 /* Check if a PDO has already been created for this device */ 1732 PdoExtension = PciFindPdoByFunction(DeviceExtension, 1733 PciSlot.u.AsULONG, 1734 PciData); 1735 if (PdoExtension) 1736 { 1737 /* Rescan scenarios are not yet implemented */ 1738 UNIMPLEMENTED_DBGBREAK(); 1739 } 1740 1741 /* Bus processing will need to happen */ 1742 ProcessFlag = TRUE; 1743 1744 /* Create the PDO for this device */ 1745 Status = PciPdoCreate(DeviceExtension, PciSlot, &DeviceObject); 1746 ASSERT(NT_SUCCESS(Status)); 1747 NewExtension = (PPCI_PDO_EXTENSION)DeviceObject->DeviceExtension; 1748 1749 /* Check for broken devices with wrong/no class codes */ 1750 if (HackFlags & PCI_HACK_FAKE_CLASS_CODE) 1751 { 1752 /* Setup a default one */ 1753 PciData->BaseClass = PCI_CLASS_BASE_SYSTEM_DEV; 1754 PciData->SubClass = PCI_SUBCLASS_SYS_OTHER; 1755 1756 /* Device will behave erratically when reading back data */ 1757 NewExtension->ExpectedWritebackFailure = TRUE; 1758 } 1759 1760 /* Clone all the information from the header */ 1761 NewExtension->VendorId = PciData->VendorID; 1762 NewExtension->DeviceId = PciData->DeviceID; 1763 NewExtension->RevisionId = PciData->RevisionID; 1764 NewExtension->ProgIf = PciData->ProgIf; 1765 NewExtension->SubClass = PciData->SubClass; 1766 NewExtension->BaseClass = PciData->BaseClass; 1767 NewExtension->HeaderType = PCI_CONFIGURATION_TYPE(PciData); 1768 1769 /* Check for modern bridge types, which are managed by the driver */ 1770 if ((NewExtension->BaseClass == PCI_CLASS_BRIDGE_DEV) && 1771 ((NewExtension->SubClass == PCI_SUBCLASS_BR_PCI_TO_PCI) || 1772 (NewExtension->SubClass == PCI_SUBCLASS_BR_CARDBUS))) 1773 { 1774 /* Acquire this device's lock */ 1775 KeEnterCriticalRegion(); 1776 KeWaitForSingleObject(&DeviceExtension->ChildListLock, 1777 Executive, 1778 KernelMode, 1779 FALSE, 1780 NULL); 1781 1782 /* Scan the bridge list until the first free entry */ 1783 for (BridgeExtension = &DeviceExtension->ChildBridgePdoList; 1784 *BridgeExtension; 1785 BridgeExtension = &(*BridgeExtension)->NextBridge); 1786 1787 /* Add this PDO as a bridge */ 1788 *BridgeExtension = NewExtension; 1789 ASSERT(NewExtension->NextBridge == NULL); 1790 1791 /* Release this device's lock */ 1792 KeSetEvent(&DeviceExtension->ChildListLock, 1793 IO_NO_INCREMENT, 1794 FALSE); 1795 KeLeaveCriticalRegion(); 1796 } 1797 1798 /* Get the PCI BIOS configuration saved in the registry */ 1799 Status = PciGetBiosConfig(NewExtension, BiosData); 1800 if (NT_SUCCESS(Status)) 1801 { 1802 /* This path has not yet been fully tested by eVb */ 1803 DPRINT1("Have BIOS configuration!\n"); 1804 UNIMPLEMENTED; 1805 1806 /* Check if the PCI BIOS configuration has changed */ 1807 if (!PcipIsSameDevice(NewExtension, BiosData)) 1808 { 1809 /* This is considered failure, and new data will be saved */ 1810 Status = STATUS_UNSUCCESSFUL; 1811 } 1812 else 1813 { 1814 /* Data is still correct, check for interrupt line change */ 1815 if (BiosData->u.type0.InterruptLine != 1816 PciData->u.type0.InterruptLine) 1817 { 1818 /* Update the current BIOS with the saved interrupt line */ 1819 PciWriteDeviceConfig(NewExtension, 1820 &BiosData->u.type0.InterruptLine, 1821 FIELD_OFFSET(PCI_COMMON_HEADER, 1822 u.type0.InterruptLine), 1823 sizeof(UCHAR)); 1824 } 1825 1826 /* Save the BIOS interrupt line and the initial command */ 1827 NewExtension->RawInterruptLine = BiosData->u.type0.InterruptLine; 1828 NewExtension->InitialCommand = BiosData->Command; 1829 } 1830 } 1831 1832 /* Check if no saved data was present or if it was a mismatch */ 1833 if (!NT_SUCCESS(Status)) 1834 { 1835 /* Save the new data */ 1836 Status = PciSaveBiosConfig(NewExtension, PciData); 1837 ASSERT(NT_SUCCESS(Status)); 1838 1839 /* Save the interrupt line and command from the device */ 1840 NewExtension->RawInterruptLine = PciData->u.type0.InterruptLine; 1841 NewExtension->InitialCommand = PciData->Command; 1842 } 1843 1844 /* Save original command from the device and hack flags */ 1845 NewExtension->CommandEnables = PciData->Command; 1846 NewExtension->HackFlags = HackFlags; 1847 1848 /* Get power, AGP, and other capability data */ 1849 PciGetEnhancedCapabilities(NewExtension, PciData); 1850 1851 /* Now configure the BARs */ 1852 Status = PciGetFunctionLimits(NewExtension, PciData, HackFlags); 1853 1854 /* Power up the device */ 1855 PciSetPowerManagedDevicePowerState(NewExtension, PowerDeviceD0, FALSE); 1856 1857 /* Apply any device hacks required for enumeration */ 1858 PciApplyHacks(DeviceExtension, 1859 PciData, 1860 PciSlot, 1861 PCI_HACK_FIXUP_AFTER_CONFIGURATION, 1862 NewExtension); 1863 1864 /* Save interrupt pin */ 1865 NewExtension->InterruptPin = PciData->u.type0.InterruptPin; 1866 1867 /* 1868 * Use either this device's actual IRQ line or, if it's connected on 1869 * a master bus whose IRQ line is actually connected to the host, use 1870 * the HAL to query the bus' IRQ line and store that as the adjusted 1871 * interrupt line instead 1872 */ 1873 NewExtension->AdjustedInterruptLine = PciGetAdjustedInterruptLine(NewExtension); 1874 1875 /* Check if this device is used for PCI debugger cards */ 1876 NewExtension->OnDebugPath = PciIsDeviceOnDebugPath(NewExtension); 1877 1878 /* Check for devices with invalid/bogus subsystem data */ 1879 if (HackFlags & PCI_HACK_NO_SUBSYSTEM) 1880 { 1881 /* Set the subsystem information to zero instead */ 1882 NewExtension->SubsystemVendorId = 0; 1883 NewExtension->SubsystemId = 0; 1884 } 1885 1886 /* Scan all capabilities */ 1887 CapOffset = NewExtension->CapabilitiesPtr; 1888 while (CapOffset) 1889 { 1890 /* Read this header */ 1891 TempOffset = PciReadDeviceCapability(NewExtension, 1892 CapOffset, 1893 0, 1894 &CapHeader, 1895 sizeof(PCI_CAPABILITIES_HEADER)); 1896 if (TempOffset != CapOffset) 1897 { 1898 /* This is a strange issue that shouldn't happen normally */ 1899 DPRINT1("PCI - Failed to read PCI capability at offset 0x%02x\n", 1900 CapOffset); 1901 ASSERT(TempOffset == CapOffset); 1902 } 1903 1904 /* Check for capabilities that this driver cares about */ 1905 switch (CapHeader.CapabilityID) 1906 { 1907 /* Power management capability is heavily used by the bus */ 1908 case PCI_CAPABILITY_ID_POWER_MANAGEMENT: 1909 1910 /* Dump the capability */ 1911 Name = "POWER"; 1912 Size = sizeof(PCI_PM_CAPABILITY); 1913 break; 1914 1915 /* AGP capability is required for AGP bus functionality */ 1916 case PCI_CAPABILITY_ID_AGP: 1917 1918 /* Dump the capability */ 1919 Name = "AGP"; 1920 Size = sizeof(PCI_AGP_CAPABILITY); 1921 break; 1922 1923 /* This driver doesn't really use anything other than that */ 1924 default: 1925 1926 /* Windows prints this, we could do a translation later */ 1927 Name = "UNKNOWN CAPABILITY"; 1928 Size = 0; 1929 break; 1930 } 1931 1932 /* Check if this is a capability that should be dumped */ 1933 if (Size) 1934 { 1935 /* Read the whole capability data */ 1936 TempOffset = PciReadDeviceCapability(NewExtension, 1937 CapOffset, 1938 CapHeader.CapabilityID, 1939 &CapHeader, 1940 Size); 1941 1942 if (TempOffset != CapOffset) 1943 { 1944 /* Again, a strange issue that shouldn't be seen */ 1945 DPRINT1("- Failed to read capability data. ***\n"); 1946 ASSERT(TempOffset == CapOffset); 1947 } 1948 } 1949 1950 /* Dump this capability */ 1951 DPRINT1("CAP @%02x ID %02x (%s)\n", 1952 CapOffset, CapHeader.CapabilityID, Name); 1953 for (i = 0; i < Size; i += 2) 1954 DPRINT1(" %04x\n", *(PUSHORT)((ULONG_PTR)&CapHeader + i)); 1955 DPRINT1("\n"); 1956 1957 /* Check the next capability */ 1958 CapOffset = CapHeader.Next; 1959 } 1960 1961 /* Check for IDE controllers */ 1962 if ((NewExtension->BaseClass == PCI_CLASS_MASS_STORAGE_CTLR) && 1963 (NewExtension->SubClass == PCI_SUBCLASS_MSC_IDE_CTLR)) 1964 { 1965 /* Do not allow them to power down completely */ 1966 NewExtension->DisablePowerDown = TRUE; 1967 } 1968 1969 /* 1970 * Check if this is a legacy bridge. Note that the i82375 PCI/EISA 1971 * bridge that is present on certain NT Alpha machines appears as 1972 * non-classified so detect it manually by scanning for its VID/PID. 1973 */ 1974 if (((NewExtension->BaseClass == PCI_CLASS_BRIDGE_DEV) && 1975 ((NewExtension->SubClass == PCI_SUBCLASS_BR_ISA) || 1976 (NewExtension->SubClass == PCI_SUBCLASS_BR_EISA) || 1977 (NewExtension->SubClass == PCI_SUBCLASS_BR_MCA))) || 1978 ((NewExtension->VendorId == 0x8086) && 1979 (NewExtension->DeviceId == 0x482))) 1980 { 1981 /* Do not allow these legacy bridges to be powered down */ 1982 NewExtension->DisablePowerDown = TRUE; 1983 } 1984 1985 /* Check if the BIOS did not configure a cache line size */ 1986 if (!PciData->CacheLineSize) 1987 { 1988 /* Check if the device is disabled */ 1989 if (!(NewExtension->CommandEnables & (PCI_ENABLE_IO_SPACE | 1990 PCI_ENABLE_MEMORY_SPACE | 1991 PCI_ENABLE_BUS_MASTER))) 1992 { 1993 /* Check if this is a PCI-X device*/ 1994 TempOffset = PciReadDeviceCapability(NewExtension, 1995 NewExtension->CapabilitiesPtr, 1996 PCI_CAPABILITY_ID_PCIX, 1997 &PcixCapHeader, 1998 sizeof(PCI_CAPABILITIES_HEADER)); 1999 2000 /* 2001 * A device with default cache line size and latency timer 2002 * settings is considered to be unconfigured. Note that on 2003 * PCI-X, the reset value of the latency timer field in the 2004 * header is 64, not 0, hence why the check for PCI-X caps 2005 * was required, and the value used here below. 2006 */ 2007 if (!(PciData->LatencyTimer) || 2008 ((TempOffset) && (PciData->LatencyTimer == 64))) 2009 { 2010 /* Keep track of the fact that it needs configuration */ 2011 DPRINT1("PCI - ScanBus, PDOx %p found unconfigured\n", 2012 NewExtension); 2013 NewExtension->NeedsHotPlugConfiguration = TRUE; 2014 } 2015 } 2016 } 2017 2018 /* Save latency and cache size information */ 2019 NewExtension->SavedLatencyTimer = PciData->LatencyTimer; 2020 NewExtension->SavedCacheLineSize = PciData->CacheLineSize; 2021 2022 /* The PDO is now ready to go */ 2023 DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING; 2024 } 2025 } 2026 2027 /* Enumeration completed, do a final pass now that all devices are found */ 2028 if (ProcessFlag) PciProcessBus(DeviceExtension); 2029 return STATUS_SUCCESS; 2030 } 2031 2032 NTSTATUS 2033 NTAPI 2034 PciQueryDeviceRelations(IN PPCI_FDO_EXTENSION DeviceExtension, 2035 IN OUT PDEVICE_RELATIONS *pDeviceRelations) 2036 { 2037 NTSTATUS Status; 2038 PPCI_PDO_EXTENSION PdoExtension; 2039 ULONG PdoCount = 0; 2040 PDEVICE_RELATIONS DeviceRelations, NewRelations; 2041 SIZE_T Size; 2042 PDEVICE_OBJECT DeviceObject, *ObjectArray; 2043 PAGED_CODE(); 2044 2045 /* Make sure the FDO is started */ 2046 ASSERT(DeviceExtension->DeviceState == PciStarted); 2047 2048 /* Synchronize while we enumerate the bus */ 2049 Status = PciBeginStateTransition(DeviceExtension, PciSynchronizedOperation); 2050 if (!NT_SUCCESS(Status)) return Status; 2051 2052 /* Scan all children PDO */ 2053 for (PdoExtension = DeviceExtension->ChildPdoList; 2054 PdoExtension; 2055 PdoExtension = PdoExtension->Next) 2056 { 2057 /* Invalidate them */ 2058 PdoExtension->NotPresent = TRUE; 2059 } 2060 2061 /* Scan the PCI Bus */ 2062 Status = PciScanBus(DeviceExtension); 2063 ASSERT(NT_SUCCESS(Status)); 2064 2065 /* Enumerate all children PDO again */ 2066 for (PdoExtension = DeviceExtension->ChildPdoList; 2067 PdoExtension; 2068 PdoExtension = PdoExtension->Next) 2069 { 2070 /* Check for PDOs that are still invalidated */ 2071 if (PdoExtension->NotPresent) 2072 { 2073 /* This means this PDO existed before, but not anymore */ 2074 PdoExtension->ReportedMissing = TRUE; 2075 DPRINT1("PCI - Old device (pdox) %p not found on rescan.\n", 2076 PdoExtension); 2077 } 2078 else 2079 { 2080 /* Increase count of detected PDOs */ 2081 PdoCount++; 2082 } 2083 } 2084 2085 /* Read the current relations and add the newly discovered relations */ 2086 DeviceRelations = *pDeviceRelations; 2087 Size = FIELD_OFFSET(DEVICE_RELATIONS, Objects) + 2088 PdoCount * sizeof(PDEVICE_OBJECT); 2089 if (DeviceRelations) Size += sizeof(PDEVICE_OBJECT) * DeviceRelations->Count; 2090 2091 /* Allocate the device relations */ 2092 NewRelations = (PDEVICE_RELATIONS)ExAllocatePoolWithTag(0, Size, 'BicP'); 2093 if (!NewRelations) 2094 { 2095 /* Out of space, cancel the operation */ 2096 PciCancelStateTransition(DeviceExtension, PciSynchronizedOperation); 2097 return STATUS_INSUFFICIENT_RESOURCES; 2098 } 2099 2100 /* Check if there were any older relations */ 2101 NewRelations->Count = 0; 2102 if (DeviceRelations) 2103 { 2104 /* Copy the old relations into the new buffer, then free the old one */ 2105 RtlCopyMemory(NewRelations, 2106 DeviceRelations, 2107 FIELD_OFFSET(DEVICE_RELATIONS, Objects) + 2108 DeviceRelations->Count * sizeof(PDEVICE_OBJECT)); 2109 ExFreePoolWithTag(DeviceRelations, 0); 2110 } 2111 2112 /* Print out that we're ready to dump relations */ 2113 DPRINT1("PCI QueryDeviceRelations/BusRelations FDOx %p (bus 0x%02x)\n", 2114 DeviceExtension, 2115 DeviceExtension->BaseBus); 2116 2117 /* Loop the current PDO children and the device relation object array */ 2118 PdoExtension = DeviceExtension->ChildPdoList; 2119 ObjectArray = &NewRelations->Objects[NewRelations->Count]; 2120 while (PdoExtension) 2121 { 2122 /* Dump this relation */ 2123 DPRINT1(" QDR PDO %p (x %p)%s\n", 2124 PdoExtension->PhysicalDeviceObject, 2125 PdoExtension, 2126 PdoExtension->NotPresent ? 2127 "<Omitted, device flaged not present>" : ""); 2128 2129 /* Is this PDO present? */ 2130 if (!PdoExtension->NotPresent) 2131 { 2132 /* Reference it and add it to the array */ 2133 DeviceObject = PdoExtension->PhysicalDeviceObject; 2134 ObReferenceObject(DeviceObject); 2135 *ObjectArray++ = DeviceObject; 2136 } 2137 2138 /* Go to the next PDO */ 2139 PdoExtension = PdoExtension->Next; 2140 } 2141 2142 /* Terminate dumping the relations */ 2143 DPRINT1(" QDR Total PDO count = %u (%u already in list)\n", 2144 NewRelations->Count + PdoCount, 2145 NewRelations->Count); 2146 2147 /* Return the final count and the new buffer */ 2148 NewRelations->Count += PdoCount; 2149 *pDeviceRelations = NewRelations; 2150 return STATUS_SUCCESS; 2151 } 2152 2153 NTSTATUS 2154 NTAPI 2155 PciSetResources(IN PPCI_PDO_EXTENSION PdoExtension, 2156 IN BOOLEAN DoReset, 2157 IN BOOLEAN SomethingSomethingDarkSide) 2158 { 2159 PPCI_FDO_EXTENSION FdoExtension; 2160 UCHAR NewCacheLineSize, NewLatencyTimer; 2161 PCI_COMMON_HEADER PciData; 2162 BOOLEAN Native; 2163 PPCI_CONFIGURATOR Configurator; 2164 2165 UNREFERENCED_PARAMETER(SomethingSomethingDarkSide); 2166 2167 /* Get the FDO and read the configuration data */ 2168 FdoExtension = PdoExtension->ParentFdoExtension; 2169 PciReadDeviceConfig(PdoExtension, &PciData, 0, PCI_COMMON_HDR_LENGTH); 2170 2171 /* Make sure this is still the same device */ 2172 if (!PcipIsSameDevice(PdoExtension, &PciData)) 2173 { 2174 /* Fail */ 2175 ASSERTMSG("PCI Set resources - not same device.\n", FALSE); 2176 return STATUS_DEVICE_DOES_NOT_EXIST; 2177 } 2178 2179 /* Nothing to set for a host bridge */ 2180 if ((PdoExtension->BaseClass == PCI_CLASS_BRIDGE_DEV) && 2181 (PdoExtension->SubClass == PCI_SUBCLASS_BR_HOST)) 2182 { 2183 /* Fake success */ 2184 return STATUS_SUCCESS; 2185 } 2186 2187 /* Check if an IDE controller is being reset */ 2188 if ((DoReset) && 2189 (PdoExtension->BaseClass == PCI_CLASS_MASS_STORAGE_CTLR) && 2190 (PdoExtension->SubClass == PCI_SUBCLASS_MSC_IDE_CTLR)) 2191 { 2192 /* Turn off native mode */ 2193 Native = PciConfigureIdeController(PdoExtension, &PciData, FALSE); 2194 ASSERT(Native == PdoExtension->IDEInNativeMode); 2195 } 2196 2197 /* Check for update of a hotplug device, or first configuration of one */ 2198 if ((PdoExtension->NeedsHotPlugConfiguration) && 2199 (FdoExtension->HotPlugParameters.Acquired)) 2200 { 2201 /* Don't have hotplug devices to test with yet, QEMU 0.14 should */ 2202 UNIMPLEMENTED_DBGBREAK(); 2203 } 2204 2205 /* Locate the correct resource configurator for this type of device */ 2206 Configurator = &PciConfigurators[PdoExtension->HeaderType]; 2207 2208 /* Apply the settings change */ 2209 Configurator->ChangeResourceSettings(PdoExtension, &PciData); 2210 2211 /* Assume no update needed */ 2212 PdoExtension->UpdateHardware = FALSE; 2213 2214 /* Check if a reset is needed */ 2215 if (DoReset) 2216 { 2217 /* Reset resources */ 2218 Configurator->ResetDevice(PdoExtension, &PciData); 2219 PciData.u.type0.InterruptLine = PdoExtension->RawInterruptLine; 2220 } 2221 2222 /* Check if the latency timer changed */ 2223 NewLatencyTimer = PdoExtension->SavedLatencyTimer; 2224 if (PciData.LatencyTimer != NewLatencyTimer) 2225 { 2226 /* Debug notification */ 2227 DPRINT1("PCI (pdox %p) changing latency from %02x to %02x.\n", 2228 PdoExtension, 2229 PciData.LatencyTimer, 2230 NewLatencyTimer); 2231 } 2232 2233 /* Check if the cache line changed */ 2234 NewCacheLineSize = PdoExtension->SavedCacheLineSize; 2235 if (PciData.CacheLineSize != NewCacheLineSize) 2236 { 2237 /* Debug notification */ 2238 DPRINT1("PCI (pdox %p) changing cache line size from %02x to %02x.\n", 2239 PdoExtension, 2240 PciData.CacheLineSize, 2241 NewCacheLineSize); 2242 } 2243 2244 /* Inherit data from PDO extension */ 2245 PciData.LatencyTimer = PdoExtension->SavedLatencyTimer; 2246 PciData.CacheLineSize = PdoExtension->SavedCacheLineSize; 2247 PciData.u.type0.InterruptLine = PdoExtension->RawInterruptLine; 2248 2249 /* Apply any resource hacks required */ 2250 PciApplyHacks(FdoExtension, 2251 &PciData, 2252 PdoExtension->Slot, 2253 PCI_HACK_FIXUP_BEFORE_UPDATE, 2254 PdoExtension); 2255 2256 /* Check if I/O space was disabled by administrator or driver */ 2257 if (PdoExtension->IoSpaceNotRequired) 2258 { 2259 /* Don't turn on the decode */ 2260 PdoExtension->CommandEnables &= ~PCI_ENABLE_IO_SPACE; 2261 } 2262 2263 /* Update the device with the new settings */ 2264 PciUpdateHardware(PdoExtension, &PciData); 2265 2266 /* Update complete */ 2267 PdoExtension->RawInterruptLine = PciData.u.type0.InterruptLine; 2268 PdoExtension->NeedsHotPlugConfiguration = FALSE; 2269 return STATUS_SUCCESS; 2270 } 2271 2272 /* EOF */ 2273