Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygenenum.c
Go to the documentation of this file.
00001 /* 00002 * PROJECT: ReactOS PCI Bus Driver 00003 * LICENSE: BSD - See COPYING.ARM in the top level directory 00004 * FILE: drivers/bus/pci/enum.c 00005 * PURPOSE: PCI Bus/Device Enumeration 00006 * PROGRAMMERS: ReactOS Portable Systems Group 00007 */ 00008 00009 /* INCLUDES *******************************************************************/ 00010 00011 #include <pci.h> 00012 #define NDEBUG 00013 #include <debug.h> 00014 00015 /* GLOBALS ********************************************************************/ 00016 00017 PIO_RESOURCE_REQUIREMENTS_LIST PciZeroIoResourceRequirements; 00018 00019 PCI_CONFIGURATOR PciConfigurators[] = 00020 { 00021 { 00022 Device_MassageHeaderForLimitsDetermination, 00023 Device_RestoreCurrent, 00024 Device_SaveLimits, 00025 Device_SaveCurrentSettings, 00026 Device_ChangeResourceSettings, 00027 Device_GetAdditionalResourceDescriptors, 00028 Device_ResetDevice 00029 }, 00030 { 00031 PPBridge_MassageHeaderForLimitsDetermination, 00032 PPBridge_RestoreCurrent, 00033 PPBridge_SaveLimits, 00034 PPBridge_SaveCurrentSettings, 00035 PPBridge_ChangeResourceSettings, 00036 PPBridge_GetAdditionalResourceDescriptors, 00037 PPBridge_ResetDevice 00038 }, 00039 { 00040 Cardbus_MassageHeaderForLimitsDetermination, 00041 Cardbus_RestoreCurrent, 00042 Cardbus_SaveLimits, 00043 Cardbus_SaveCurrentSettings, 00044 Cardbus_ChangeResourceSettings, 00045 Cardbus_GetAdditionalResourceDescriptors, 00046 Cardbus_ResetDevice 00047 } 00048 }; 00049 00050 /* FUNCTIONS ******************************************************************/ 00051 00052 BOOLEAN 00053 NTAPI 00054 PciComputeNewCurrentSettings(IN PPCI_PDO_EXTENSION PdoExtension, 00055 IN PCM_RESOURCE_LIST ResourceList) 00056 { 00057 PCM_PARTIAL_RESOURCE_DESCRIPTOR Partial, InterruptResource; 00058 PCM_PARTIAL_RESOURCE_DESCRIPTOR BaseResource, CurrentDescriptor; 00059 PCM_PARTIAL_RESOURCE_DESCRIPTOR PreviousDescriptor; 00060 CM_PARTIAL_RESOURCE_DESCRIPTOR ResourceArray[7]; 00061 PCM_FULL_RESOURCE_DESCRIPTOR FullList; 00062 BOOLEAN DrainPartial, RangeChange; 00063 ULONG i, j; 00064 PPCI_FUNCTION_RESOURCES PciResources; 00065 PAGED_CODE(); 00066 00067 /* Make sure we have either no resources, or at least one */ 00068 ASSERT((ResourceList == NULL) || (ResourceList->Count == 1)); 00069 00070 /* Initialize no partial, interrupt descriptor, or range change */ 00071 Partial = NULL; 00072 InterruptResource = NULL; 00073 RangeChange = FALSE; 00074 00075 /* Check if there's not actually any resources */ 00076 if (!(ResourceList) || !(ResourceList->Count)) 00077 { 00078 /* Then just return the hardware update state */ 00079 return PdoExtension->UpdateHardware; 00080 } 00081 00082 /* Print the new specified resource list */ 00083 PciDebugPrintCmResList(ResourceList); 00084 00085 /* Clear the temporary resource array */ 00086 for (i = 0; i < 7; i++) ResourceArray[i].Type = CmResourceTypeNull; 00087 00088 /* Loop the full resource descriptor */ 00089 FullList = ResourceList->List; 00090 for (i = 0; i < ResourceList->Count; i++) 00091 { 00092 /* Initialize loop variables */ 00093 DrainPartial = FALSE; 00094 BaseResource = NULL; 00095 00096 /* Loop the partial descriptors */ 00097 Partial = FullList->PartialResourceList.PartialDescriptors; 00098 for (j = 0; j < FullList->PartialResourceList.Count; j++) 00099 { 00100 /* Check if we were supposed to drain a partial due to device data */ 00101 if (DrainPartial) 00102 { 00103 /* Draining complete, move on to the next descriptor then */ 00104 DrainPartial--; 00105 continue; 00106 } 00107 00108 /* Check what kind of descriptor this was */ 00109 switch (Partial->Type) 00110 { 00111 /* Base BAR resources */ 00112 case CmResourceTypePort: 00113 case CmResourceTypeMemory: 00114 00115 /* Set it as the base */ 00116 ASSERT(BaseResource == NULL); 00117 BaseResource = Partial; 00118 break; 00119 00120 /* Interrupt resource */ 00121 case CmResourceTypeInterrupt: 00122 00123 /* Make sure it's a compatible (and the only) PCI interrupt */ 00124 ASSERT(InterruptResource == NULL); 00125 ASSERT(Partial->u.Interrupt.Level == Partial->u.Interrupt.Vector); 00126 InterruptResource = Partial; 00127 00128 /* Only 255 interrupts on x86/x64 hardware */ 00129 if (Partial->u.Interrupt.Level < 256) 00130 { 00131 /* Use the passed interrupt line */ 00132 PdoExtension->AdjustedInterruptLine = Partial->u.Interrupt.Level; 00133 } 00134 else 00135 { 00136 /* Invalid vector, so ignore it */ 00137 PdoExtension->AdjustedInterruptLine = 0; 00138 } 00139 00140 break; 00141 00142 /* Check for specific device data */ 00143 case CmResourceTypeDevicePrivate: 00144 00145 /* Check what kind of data this was */ 00146 switch (Partial->u.DevicePrivate.Data[0]) 00147 { 00148 /* Not used in the driver yet */ 00149 case 1: 00150 UNIMPLEMENTED; 00151 while (TRUE); 00152 break; 00153 00154 /* Not used in the driver yet */ 00155 case 2: 00156 UNIMPLEMENTED; 00157 while (TRUE); 00158 break; 00159 00160 /* A drain request */ 00161 case 3: 00162 /* Shouldn't be a base resource, this is a drain */ 00163 ASSERT(BaseResource == NULL); 00164 DrainPartial = Partial->u.DevicePrivate.Data[1]; 00165 ASSERT(DrainPartial == TRUE); 00166 break; 00167 } 00168 break; 00169 } 00170 00171 /* Move to the next descriptor */ 00172 Partial = PciNextPartialDescriptor(Partial); 00173 } 00174 00175 /* We should be starting a new list now */ 00176 ASSERT(BaseResource == NULL); 00177 FullList = (PVOID)Partial; 00178 } 00179 00180 /* Check the current assigned PCI resources */ 00181 PciResources = PdoExtension->Resources; 00182 if (!PciResources) return FALSE; 00183 00184 //if... // MISSING CODE 00185 UNIMPLEMENTED; 00186 DPRINT1("Missing sanity checking code!\n"); 00187 00188 /* Loop all the PCI function resources */ 00189 for (i = 0; i < 7; i++) 00190 { 00191 /* Get the current function resource descriptor, and the new one */ 00192 CurrentDescriptor = &PciResources->Current[i]; 00193 Partial = &ResourceArray[i]; 00194 00195 /* Previous is current during the first loop iteration */ 00196 PreviousDescriptor = &PciResources->Current[(i == 0) ? (0) : (i - 1)]; 00197 00198 /* Check if this new descriptor is different than the old one */ 00199 if (((Partial->Type != CurrentDescriptor->Type) || 00200 (Partial->Type != CmResourceTypeNull)) && 00201 ((Partial->u.Generic.Start.QuadPart != 00202 CurrentDescriptor->u.Generic.Start.QuadPart) || 00203 (Partial->u.Generic.Length != CurrentDescriptor->u.Generic.Length))) 00204 { 00205 /* Record a change */ 00206 RangeChange = TRUE; 00207 00208 /* Was there a range before? */ 00209 if (CurrentDescriptor->Type != CmResourceTypeNull) 00210 { 00211 /* Print it */ 00212 DbgPrint(" Old range-\n"); 00213 PciDebugPrintPartialResource(CurrentDescriptor); 00214 } 00215 else 00216 { 00217 /* There was no range */ 00218 DbgPrint(" Previously unset range\n"); 00219 } 00220 00221 /* Print new one */ 00222 DbgPrint(" changed to\n"); 00223 PciDebugPrintPartialResource(Partial); 00224 00225 /* Update to new range */ 00226 CurrentDescriptor->Type = Partial->Type; 00227 PreviousDescriptor->u.Generic.Start = Partial->u.Generic.Start; 00228 PreviousDescriptor->u.Generic.Length = Partial->u.Generic.Length; 00229 CurrentDescriptor = PreviousDescriptor; 00230 } 00231 } 00232 00233 /* Either the hardware was updated, or a resource range changed */ 00234 return ((RangeChange) || (PdoExtension->UpdateHardware)); 00235 } 00236 00237 VOID 00238 NTAPI 00239 PcipUpdateHardware(IN PVOID Context, 00240 IN PVOID Context2) 00241 { 00242 PPCI_PDO_EXTENSION PdoExtension = Context; 00243 PPCI_COMMON_HEADER PciData = Context2; 00244 00245 /* Check if we're allowed to disable decodes */ 00246 PciData->Command = PdoExtension->CommandEnables; 00247 if (!(PdoExtension->HackFlags & PCI_HACK_PRESERVE_COMMAND)) 00248 { 00249 /* Disable all decodes */ 00250 PciData->Command &= ~(PCI_ENABLE_IO_SPACE | 00251 PCI_ENABLE_MEMORY_SPACE | 00252 PCI_ENABLE_BUS_MASTER | 00253 PCI_ENABLE_WRITE_AND_INVALIDATE); 00254 } 00255 00256 /* Update the device configuration */ 00257 PciData->Status = 0; 00258 PciWriteDeviceConfig(PdoExtension, PciData, 0, PCI_COMMON_HDR_LENGTH); 00259 00260 /* Turn decodes back on */ 00261 PciDecodeEnable(PdoExtension, TRUE, &PdoExtension->CommandEnables); 00262 } 00263 00264 VOID 00265 NTAPI 00266 PciUpdateHardware(IN PPCI_PDO_EXTENSION PdoExtension, 00267 IN PPCI_COMMON_HEADER PciData) 00268 { 00269 PCI_IPI_CONTEXT Context; 00270 00271 /* Check for critical devices and PCI Debugging devices */ 00272 if ((PdoExtension->HackFlags & PCI_HACK_CRITICAL_DEVICE) || 00273 (PdoExtension->OnDebugPath)) 00274 { 00275 /* Build the context and send an IPI */ 00276 Context.RunCount = 1; 00277 Context.Barrier = 1; 00278 Context.Context = PciData; 00279 Context.Function = PcipUpdateHardware; 00280 Context.DeviceExtension = PdoExtension; 00281 KeIpiGenericCall(PciExecuteCriticalSystemRoutine, (ULONG_PTR)&Context); 00282 } 00283 else 00284 { 00285 /* Just to the update inline */ 00286 PcipUpdateHardware(PdoExtension, PciData); 00287 } 00288 } 00289 00290 PIO_RESOURCE_REQUIREMENTS_LIST 00291 NTAPI 00292 PciAllocateIoRequirementsList(IN ULONG Count, 00293 IN ULONG BusNumber, 00294 IN ULONG SlotNumber) 00295 { 00296 SIZE_T Size; 00297 PIO_RESOURCE_REQUIREMENTS_LIST RequirementsList; 00298 00299 /* Calculate the final size of the list, including each descriptor */ 00300 Size = sizeof(IO_RESOURCE_REQUIREMENTS_LIST); 00301 if (Count > 1) Size = sizeof(IO_RESOURCE_DESCRIPTOR) * (Count - 1) + 00302 sizeof(IO_RESOURCE_REQUIREMENTS_LIST); 00303 00304 /* Allocate the list */ 00305 RequirementsList = ExAllocatePoolWithTag(PagedPool, Size, 'BicP'); 00306 if (!RequirementsList) return NULL; 00307 00308 /* Initialize it */ 00309 RtlZeroMemory(RequirementsList, Size); 00310 RequirementsList->AlternativeLists = 1; 00311 RequirementsList->BusNumber = BusNumber; 00312 RequirementsList->SlotNumber = SlotNumber; 00313 RequirementsList->InterfaceType = PCIBus; 00314 RequirementsList->ListSize = Size; 00315 RequirementsList->List[0].Count = Count; 00316 RequirementsList->List[0].Version = 1; 00317 RequirementsList->List[0].Revision = 1; 00318 00319 /* Return it */ 00320 return RequirementsList; 00321 } 00322 00323 PCM_RESOURCE_LIST 00324 NTAPI 00325 PciAllocateCmResourceList(IN ULONG Count, 00326 IN ULONG BusNumber) 00327 { 00328 SIZE_T Size; 00329 PCM_RESOURCE_LIST ResourceList; 00330 00331 /* Calculate the final size of the list, including each descriptor */ 00332 Size = sizeof(CM_RESOURCE_LIST); 00333 if (Count > 1) Size = sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR) * (Count - 1) + 00334 sizeof(CM_RESOURCE_LIST); 00335 00336 /* Allocate the list */ 00337 ResourceList = ExAllocatePoolWithTag(PagedPool, Size, 'BicP'); 00338 if (!ResourceList) return NULL; 00339 00340 /* Initialize it */ 00341 RtlZeroMemory(ResourceList, Size); 00342 ResourceList->Count = 1; 00343 ResourceList->List[0].BusNumber = BusNumber; 00344 ResourceList->List[0].InterfaceType = PCIBus; 00345 ResourceList->List[0].PartialResourceList.Version = 1; 00346 ResourceList->List[0].PartialResourceList.Revision = 1; 00347 ResourceList->List[0].PartialResourceList.Count = Count; 00348 00349 /* Return it */ 00350 return ResourceList; 00351 } 00352 00353 NTSTATUS 00354 NTAPI 00355 PciQueryResources(IN PPCI_PDO_EXTENSION PdoExtension, 00356 OUT PCM_RESOURCE_LIST *Buffer) 00357 { 00358 PPCI_FUNCTION_RESOURCES PciResources; 00359 BOOLEAN HaveVga, HaveMemSpace, HaveIoSpace; 00360 USHORT BridgeControl, PciCommand; 00361 ULONG Count, i; 00362 PCM_PARTIAL_RESOURCE_DESCRIPTOR Partial, Resource, LastResource; 00363 PCM_RESOURCE_LIST ResourceList; 00364 UCHAR InterruptLine; 00365 PAGED_CODE(); 00366 00367 /* Assume failure */ 00368 Count = 0; 00369 HaveVga = FALSE; 00370 *Buffer = NULL; 00371 00372 /* Make sure there's some resources to query */ 00373 PciResources = PdoExtension->Resources; 00374 if (!PciResources) return STATUS_SUCCESS; 00375 00376 /* Read the decodes */ 00377 PciReadDeviceConfig(PdoExtension, 00378 &PciCommand, 00379 FIELD_OFFSET(PCI_COMMON_HEADER, Command), 00380 sizeof(USHORT)); 00381 00382 /* Check which ones are turned on */ 00383 HaveIoSpace = PciCommand & PCI_ENABLE_IO_SPACE; 00384 HaveMemSpace = PciCommand & PCI_ENABLE_MEMORY_SPACE; 00385 00386 /* Loop maximum possible descriptors */ 00387 for (i = 0; i < 7; i++) 00388 { 00389 /* Check if the decode for this descriptor is actually turned on */ 00390 Partial = &PciResources->Current[i]; 00391 if (((HaveMemSpace) && (Partial->Type == CmResourceTypeMemory)) || 00392 ((HaveIoSpace) && (Partial->Type == CmResourceTypePort))) 00393 { 00394 /* One more fully active descriptor */ 00395 Count++; 00396 } 00397 } 00398 00399 /* If there's an interrupt pin associated, check at least one decode is on */ 00400 if ((PdoExtension->InterruptPin) && ((HaveMemSpace) || (HaveIoSpace))) 00401 { 00402 /* Read the interrupt line for the pin, add a descriptor if it's valid */ 00403 InterruptLine = PdoExtension->AdjustedInterruptLine; 00404 if ((InterruptLine) && (InterruptLine != -1)) Count++; 00405 } 00406 00407 /* Check for PCI bridge */ 00408 if (PdoExtension->HeaderType == PCI_BRIDGE_TYPE) 00409 { 00410 /* Read bridge settings, check if VGA is present */ 00411 PciReadDeviceConfig(PdoExtension, 00412 &BridgeControl, 00413 FIELD_OFFSET(PCI_COMMON_HEADER, u.type1.BridgeControl), 00414 sizeof(USHORT)); 00415 if (BridgeControl & PCI_ENABLE_BRIDGE_VGA) 00416 { 00417 /* Remember for later */ 00418 HaveVga = TRUE; 00419 00420 /* One memory descriptor for 0xA0000, plus the two I/O port ranges */ 00421 if (HaveMemSpace) Count++; 00422 if (HaveIoSpace) Count += 2; 00423 } 00424 } 00425 00426 /* If there's no descriptors in use, there's no resources, so return */ 00427 if (!Count) return STATUS_SUCCESS; 00428 00429 /* Allocate a resource list to hold the resources */ 00430 ResourceList = PciAllocateCmResourceList(Count, 00431 PdoExtension->ParentFdoExtension->BaseBus); 00432 if (!ResourceList) return STATUS_INSUFFICIENT_RESOURCES; 00433 00434 /* This is where the descriptors will be copied into */ 00435 Resource = ResourceList->List[0].PartialResourceList.PartialDescriptors; 00436 LastResource = Resource + Count + 1; 00437 00438 /* Loop maximum possible descriptors */ 00439 for (i = 0; i < 7; i++) 00440 { 00441 /* Check if the decode for this descriptor is actually turned on */ 00442 Partial = &PciResources->Current[i]; 00443 if (((HaveMemSpace) && (Partial->Type == CmResourceTypeMemory)) || 00444 ((HaveIoSpace) && (Partial->Type == CmResourceTypePort))) 00445 { 00446 /* Copy the descriptor into the resource list */ 00447 *Resource++ = *Partial; 00448 } 00449 } 00450 00451 /* Check if earlier the code detected this was a PCI bridge with VGA on it */ 00452 if (HaveVga) 00453 { 00454 /* Are the memory decodes enabled? */ 00455 if (HaveMemSpace) 00456 { 00457 /* Build a memory descriptor for a 128KB framebuffer at 0xA0000 */ 00458 Resource->Flags = CM_RESOURCE_MEMORY_READ_WRITE; 00459 Resource->u.Generic.Start.HighPart = 0; 00460 Resource->Type = CmResourceTypeMemory; 00461 Resource->u.Generic.Start.LowPart = 0xA0000; 00462 Resource->u.Generic.Length = 0x20000; 00463 Resource++; 00464 } 00465 00466 /* Are the I/O decodes enabled? */ 00467 if (HaveIoSpace) 00468 { 00469 /* Build an I/O descriptor for the graphic ports at 0x3B0 */ 00470 Resource->Type = CmResourceTypePort; 00471 Resource->Flags = CM_RESOURCE_PORT_POSITIVE_DECODE | CM_RESOURCE_PORT_10_BIT_DECODE; 00472 Resource->u.Port.Start.QuadPart = 0x3B0u; 00473 Resource->u.Port.Length = 0xC; 00474 Resource++; 00475 00476 /* Build an I/O descriptor for the graphic ports at 0x3C0 */ 00477 Resource->Type = CmResourceTypePort; 00478 Resource->Flags = CM_RESOURCE_PORT_POSITIVE_DECODE | CM_RESOURCE_PORT_10_BIT_DECODE; 00479 Resource->u.Port.Start.QuadPart = 0x3C0u; 00480 Resource->u.Port.Length = 0x20; 00481 Resource++; 00482 } 00483 } 00484 00485 /* If there's an interrupt pin associated, check at least one decode is on */ 00486 if ((PdoExtension->InterruptPin) && ((HaveMemSpace) || (HaveIoSpace))) 00487 { 00488 /* Read the interrupt line for the pin, check if it's valid */ 00489 InterruptLine = PdoExtension->AdjustedInterruptLine; 00490 if ((InterruptLine) && (InterruptLine != -1)) 00491 { 00492 /* Make sure there's still space */ 00493 ASSERT(Resource < LastResource); 00494 00495 /* Add the interrupt descriptor */ 00496 Resource->Flags = CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE; 00497 Resource->Type = CmResourceTypeInterrupt; 00498 Resource->ShareDisposition = CmResourceShareShared; 00499 Resource->u.Interrupt.Affinity = -1; 00500 Resource->u.Interrupt.Level = InterruptLine; 00501 Resource->u.Interrupt.Vector = InterruptLine; 00502 } 00503 } 00504 00505 /* Return the resouce list */ 00506 *Buffer = ResourceList; 00507 return STATUS_SUCCESS; 00508 } 00509 00510 NTSTATUS 00511 NTAPI 00512 PciQueryTargetDeviceRelations(IN PPCI_PDO_EXTENSION PdoExtension, 00513 IN OUT PDEVICE_RELATIONS *pDeviceRelations) 00514 { 00515 PDEVICE_RELATIONS DeviceRelations; 00516 PAGED_CODE(); 00517 00518 /* If there were existing relations, free them */ 00519 if (*pDeviceRelations) ExFreePoolWithTag(*pDeviceRelations, 0); 00520 00521 /* Allocate a new structure for the relations */ 00522 DeviceRelations = ExAllocatePoolWithTag(NonPagedPool, 00523 sizeof(DEVICE_RELATIONS), 00524 'BicP'); 00525 if (!DeviceRelations) return STATUS_INSUFFICIENT_RESOURCES; 00526 00527 /* Only one relation: the PDO */ 00528 DeviceRelations->Count = 1; 00529 DeviceRelations->Objects[0] = PdoExtension->PhysicalDeviceObject; 00530 ObReferenceObject(DeviceRelations->Objects[0]); 00531 00532 /* Return the new relations */ 00533 *pDeviceRelations = DeviceRelations; 00534 return STATUS_SUCCESS; 00535 } 00536 00537 NTSTATUS 00538 NTAPI 00539 PciQueryEjectionRelations(IN PPCI_PDO_EXTENSION PdoExtension, 00540 IN OUT PDEVICE_RELATIONS *pDeviceRelations) 00541 { 00542 /* Not yet implemented */ 00543 UNIMPLEMENTED; 00544 while (TRUE); 00545 } 00546 00547 NTSTATUS 00548 NTAPI 00549 PciBuildRequirementsList(IN PPCI_PDO_EXTENSION PdoExtension, 00550 IN PPCI_COMMON_HEADER PciData, 00551 OUT PIO_RESOURCE_REQUIREMENTS_LIST* Buffer) 00552 { 00553 PIO_RESOURCE_REQUIREMENTS_LIST RequirementsList; 00554 { 00555 /* There aren't, so use the zero descriptor */ 00556 RequirementsList = PciZeroIoResourceRequirements; 00557 00558 /* Does it actually exist yet? */ 00559 if (!PciZeroIoResourceRequirements) 00560 { 00561 /* Allocate it, and use it for future use */ 00562 RequirementsList = PciAllocateIoRequirementsList(0, 0, 0); 00563 PciZeroIoResourceRequirements = RequirementsList; 00564 if (!PciZeroIoResourceRequirements) return STATUS_INSUFFICIENT_RESOURCES; 00565 } 00566 00567 /* Return the zero requirements list to the caller */ 00568 *Buffer = RequirementsList; 00569 DPRINT1("PCI - build resource reqs - early out, 0 resources\n"); 00570 return STATUS_SUCCESS; 00571 } 00572 return STATUS_SUCCESS; 00573 } 00574 00575 NTSTATUS 00576 NTAPI 00577 PciQueryRequirements(IN PPCI_PDO_EXTENSION PdoExtension, 00578 IN OUT PIO_RESOURCE_REQUIREMENTS_LIST *RequirementsList) 00579 { 00580 NTSTATUS Status; 00581 PCI_COMMON_HEADER PciHeader; 00582 PAGED_CODE(); 00583 00584 /* Check if the PDO has any resources, or at least an interrupt pin */ 00585 if ((PdoExtension->Resources) || (PdoExtension->InterruptPin)) 00586 { 00587 /* Read the current PCI header */ 00588 PciReadDeviceConfig(PdoExtension, &PciHeader, 0, PCI_COMMON_HDR_LENGTH); 00589 00590 /* Use it to build a list of requirements */ 00591 Status = PciBuildRequirementsList(PdoExtension, &PciHeader, RequirementsList); 00592 if (!NT_SUCCESS(Status)) return Status; 00593 00594 /* Is this a Compaq PCI Hotplug Controller (r17) on a PAE system ? */ 00595 if ((PciHeader.VendorID == 0xE11) && 00596 (PciHeader.DeviceID == 0xA0F7) && 00597 (PciHeader.RevisionID == 17) && 00598 (ExIsProcessorFeaturePresent(PF_PAE_ENABLED))) 00599 { 00600 /* Have not tested this on eVb's machine yet */ 00601 UNIMPLEMENTED; 00602 while (TRUE); 00603 } 00604 00605 /* Check if the requirements are actually the zero list */ 00606 if (*RequirementsList == PciZeroIoResourceRequirements) 00607 { 00608 /* A simple NULL will sufficie for the PnP Manager */ 00609 *RequirementsList = NULL; 00610 DPRINT1("Returning NULL requirements list\n"); 00611 } 00612 else 00613 { 00614 /* Otherwise, print out the requirements list */ 00615 PciDebugPrintIoResReqList(*RequirementsList); 00616 } 00617 } 00618 else 00619 { 00620 /* There aren't any resources, so simply return NULL */ 00621 DPRINT1("PciQueryRequirements returning NULL requirements list\n"); 00622 *RequirementsList = NULL; 00623 } 00624 00625 /* This call always succeeds (but maybe with no requirements) */ 00626 return STATUS_SUCCESS; 00627 } 00628 00629 /* 00630 * 7. The IO/MEM/Busmaster decodes are disabled for the device. 00631 * 8. The PCI bus driver sets the operating mode bits of the Programming 00632 * Interface byte to switch the controller to native mode. 00633 * 00634 * Important: When the controller is set to native mode, it must quiet itself 00635 * and must not decode I/O resources or generate interrupts until the operating 00636 * system has enabled the ports in the PCI configuration header. 00637 * The IO/MEM/BusMaster bits will be disabled before the mode change, but it 00638 * is not possible to disable interrupts on the device. The device must not 00639 * generate interrupts (either legacy or native mode) while the decodes are 00640 * disabled in the command register. 00641 * 00642 * This operation is expected to be instantaneous and the operating system does 00643 * not stall afterward. It is also expected that the interrupt pin register in 00644 * the PCI Configuration space for this device is accurate. The operating system 00645 * re-reads this data after previously ignoring it. 00646 */ 00647 BOOLEAN 00648 NTAPI 00649 PciConfigureIdeController(IN PPCI_PDO_EXTENSION PdoExtension, 00650 IN PPCI_COMMON_HEADER PciData, 00651 IN BOOLEAN Initial) 00652 { 00653 UCHAR MasterMode, SlaveMode, MasterFixed, SlaveFixed, ProgIf, NewProgIf; 00654 BOOLEAN Switched; 00655 USHORT Command; 00656 00657 /* Assume it won't work */ 00658 Switched = FALSE; 00659 00660 /* Get master and slave current settings, and programmability flag */ 00661 ProgIf = PciData->ProgIf; 00662 MasterMode = (ProgIf & 1) == 1; 00663 MasterFixed = (ProgIf & 2) == 0; 00664 SlaveMode = (ProgIf & 4) == 4; 00665 SlaveFixed = (ProgIf & 8) == 0; 00666 00667 /* 00668 * [..] In order for Windows XP SP1 and Windows Server 2003 to switch an ATA 00669 * ATA controller from compatible mode to native mode, the following must be 00670 * true: 00671 * 00672 * - The controller must indicate in its programming interface that both channels 00673 * can be switched to native mode. Windows XP SP1 and Windows Server 2003 do 00674 * not support switching only one IDE channel to native mode. See the PCI IDE 00675 * Controller Specification Revision 1.0 for details. 00676 */ 00677 if ((MasterMode != SlaveMode) || (MasterFixed != SlaveFixed)) 00678 { 00679 /* Windows does not support this configuration, fail */ 00680 DPRINT1("PCI: Warning unsupported IDE controller configuration for VEN_%04x&DEV_%04x!", 00681 PdoExtension->VendorId, 00682 PdoExtension->DeviceId); 00683 return Switched; 00684 } 00685 00686 /* Check if the controller is already in native mode */ 00687 if ((MasterMode) && (SlaveMode)) 00688 { 00689 /* Check if I/O decodes should be disabled */ 00690 if ((Initial) || (PdoExtension->IoSpaceUnderNativeIdeControl)) 00691 { 00692 /* Read the current command */ 00693 PciReadDeviceConfig(PdoExtension, 00694 &Command, 00695 FIELD_OFFSET(PCI_COMMON_HEADER, Command), 00696 sizeof(USHORT)); 00697 00698 /* Disable I/O space decode */ 00699 Command &= ~PCI_ENABLE_IO_SPACE; 00700 00701 /* Update new command in PCI IDE controller */ 00702 PciWriteDeviceConfig(PdoExtension, 00703 &Command, 00704 FIELD_OFFSET(PCI_COMMON_HEADER, Command), 00705 sizeof(USHORT)); 00706 00707 /* Save updated command value */ 00708 PciData->Command = Command; 00709 } 00710 00711 /* The controller is now in native mode */ 00712 Switched = TRUE; 00713 } 00714 else if (!(MasterFixed) && 00715 !(SlaveFixed) && 00716 (PdoExtension->BIOSAllowsIDESwitchToNativeMode) && 00717 !(PdoExtension->HackFlags & PCI_HACK_DISABLE_IDE_NATIVE_MODE)) 00718 { 00719 /* Turn off decodes */ 00720 PciDecodeEnable(PdoExtension, FALSE, NULL); 00721 00722 /* Update the current command */ 00723 PciReadDeviceConfig(PdoExtension, 00724 &PciData->Command, 00725 FIELD_OFFSET(PCI_COMMON_HEADER, Command), 00726 sizeof(USHORT)); 00727 00728 /* Enable native mode */ 00729 ProgIf = PciData->ProgIf | 5; 00730 PciWriteDeviceConfig(PdoExtension, 00731 &ProgIf, 00732 FIELD_OFFSET(PCI_COMMON_HEADER, ProgIf), 00733 sizeof(UCHAR)); 00734 00735 /* Verify the setting "stuck" */ 00736 PciReadDeviceConfig(PdoExtension, 00737 &NewProgIf, 00738 FIELD_OFFSET(PCI_COMMON_HEADER, ProgIf), 00739 sizeof(UCHAR)); 00740 if (NewProgIf == ProgIf) 00741 { 00742 /* Update the header and PDO data with the new programming mode */ 00743 PciData->ProgIf = ProgIf; 00744 PdoExtension->ProgIf = NewProgIf; 00745 00746 /* Clear the first four BARs to reset current BAR setttings */ 00747 PciData->u.type0.BaseAddresses[0] = 0; 00748 PciData->u.type0.BaseAddresses[1] = 0; 00749 PciData->u.type0.BaseAddresses[2] = 0; 00750 PciData->u.type0.BaseAddresses[3] = 0; 00751 PciWriteDeviceConfig(PdoExtension, 00752 PciData->u.type0.BaseAddresses, 00753 FIELD_OFFSET(PCI_COMMON_HEADER, 00754 u.type0.BaseAddresses), 00755 4 * sizeof(ULONG)); 00756 00757 /* Re-read the BARs to have the latest data for native mode IDE */ 00758 PciReadDeviceConfig(PdoExtension, 00759 PciData->u.type0.BaseAddresses, 00760 FIELD_OFFSET(PCI_COMMON_HEADER, 00761 u.type0.BaseAddresses), 00762 4 * sizeof(ULONG)); 00763 00764 /* Re-read the interrupt pin used for native mode IDE */ 00765 PciReadDeviceConfig(PdoExtension, 00766 &PciData->u.type0.InterruptPin, 00767 FIELD_OFFSET(PCI_COMMON_HEADER, 00768 u.type0.InterruptPin), 00769 sizeof(UCHAR)); 00770 00771 /* The IDE Controller is now in native mode */ 00772 Switched = TRUE; 00773 } 00774 else 00775 { 00776 /* Settings did not work, fail */ 00777 DPRINT1("PCI: Warning failed switch to native mode for IDE controller VEN_%04x&DEV_%04x!", 00778 PciData->VendorID, 00779 PciData->DeviceID); 00780 } 00781 } 00782 00783 /* Return whether or not native mode was enabled on the IDE controller */ 00784 return Switched; 00785 } 00786 00787 VOID 00788 NTAPI 00789 PciApplyHacks(IN PPCI_FDO_EXTENSION DeviceExtension, 00790 IN PPCI_COMMON_HEADER PciData, 00791 IN PCI_SLOT_NUMBER SlotNumber, 00792 IN ULONG OperationType, 00793 PPCI_PDO_EXTENSION PdoExtension) 00794 { 00795 ULONG LegacyBaseAddress; 00796 USHORT Command; 00797 UCHAR RegValue; 00798 00799 /* Check what kind of hack operation this is */ 00800 switch (OperationType) 00801 { 00802 /* 00803 * This is mostly concerned with fixing up incorrect class data that can 00804 * exist on certain PCI hardware before the 2.0 spec was ratified. 00805 */ 00806 case PCI_HACK_FIXUP_BEFORE_CONFIGURATION: 00807 00808 /* Note that the i82375 PCI/EISA and the i82378 PCI/ISA bridges that 00809 * are present on certain DEC/NT Alpha machines are pre-PCI 2.0 devices 00810 * and appear as non-classified, so their correct class/subclass data 00811 * is written here instead. 00812 */ 00813 if ((PciData->VendorID == 0x8086) && 00814 ((PciData->DeviceID == 0x482) || (PciData->DeviceID == 0x484))) 00815 { 00816 /* Note that 0x482 is the i82375 (EISA), 0x484 is the i82378 (ISA) */ 00817 PciData->SubClass = PciData->DeviceID == 0x482 ? 00818 PCI_SUBCLASS_BR_EISA : PCI_SUBCLASS_BR_ISA; 00819 PciData->BaseClass = PCI_CLASS_BRIDGE_DEV; 00820 00821 /* 00822 * Because the software is modifying the actual header data from 00823 * the BIOS, this flag tells the driver to ignore failures when 00824 * comparing the original BIOS data with the PCI data. 00825 */ 00826 if (PdoExtension) PdoExtension->ExpectedWritebackFailure = TRUE; 00827 } 00828 00829 /* Note that in this case, an immediate return is issued */ 00830 return; 00831 00832 /* 00833 * This is concerned with setting up interrupts correctly for native IDE 00834 * mode, but will also handle broken VGA decoding on older bridges as 00835 * well as a PAE-specific hack for certain Compaq Hot-Plug Controllers. 00836 */ 00837 case PCI_HACK_FIXUP_AFTER_CONFIGURATION: 00838 00839 /* There should always be a PDO extension passed in */ 00840 ASSERT(PdoExtension); 00841 00842 /* 00843 * On the OPTi Viper-M IDE controller, Linux doesn't support IDE-DMA 00844 * and FreeBSD bug reports indicate that the system crashes when the 00845 * feature is enabled (so it's disabled on that OS as well). In the 00846 * NT PCI Bus Driver, it seems Microsoft too, completely disables 00847 * Native IDE functionality on this controller, so it would seem OPTi 00848 * simply frelled up this controller. 00849 */ 00850 if ((PciData->VendorID == 0x1045) && (PciData->DeviceID != 0xC621)) 00851 { 00852 /* Disable native mode */ 00853 PciData->ProgIf &= ~5; 00854 PciData->u.type0.InterruptPin = 0; 00855 00856 /* 00857 * Because the software is modifying the actual header data from 00858 * the BIOS, this flag tells the driver to ignore failures when 00859 * comparing the original BIOS data with the PCI data. 00860 */ 00861 PdoExtension->ExpectedWritebackFailure = TRUE; 00862 } 00863 else if ((PciData->BaseClass == PCI_CLASS_MASS_STORAGE_CTLR) && 00864 (PciData->SubClass == PCI_SUBCLASS_MSC_IDE_CTLR)) 00865 { 00866 /* For other IDE controllers, start out in compatible mode */ 00867 PdoExtension->BIOSAllowsIDESwitchToNativeMode = FALSE; 00868 00869 /* 00870 * Registry must have enabled native mode (typically as a result 00871 * of an INF file directive part of the IDE controller's driver) 00872 * and the system must not be booted in Safe Mode. If that checks 00873 * out, then evaluate the ACPI NATA method to see if the platform 00874 * supports this. See the section "BIOS and Platform Prerequisites 00875 * for Switching a Native-Mode-Capable Controller" in the Storage 00876 * section of the Windows Driver Kit for more details: 00877 * 00878 * 5. For each ATA controller enumerated, the PCI bus driver checks 00879 * the Programming Interface register of the IDE controller to 00880 * see if it supports switching both channels to native mode. 00881 * 6. The PCI bus driver checks whether the BIOS/platform supports 00882 * switching the controller by checking the NATA method described 00883 * earlier in this article. 00884 * 00885 * If an ATA controller does not indicate that it is native 00886 * mode-capable, or if the BIOS NATA control method is missing 00887 * or does not list that device, the PCI bus driver does not 00888 * switch the controller and it is assigned legacy resources. 00889 * 00890 * If both the controller and the BIOS indicate that the controller 00891 * can be switched, the process of switching the controller begins 00892 * with the next step. 00893 */ 00894 if ((PciEnableNativeModeATA) && 00895 !(InitSafeBootMode) && 00896 (PciIsSlotPresentInParentMethod(PdoExtension, 'ATAN'))) 00897 { 00898 /* The platform supports it, remember that */ 00899 PdoExtension->BIOSAllowsIDESwitchToNativeMode = TRUE; 00900 00901 /* 00902 * Now switch the controller into native mode if both channels 00903 * support native IDE mode. See "How Windows Switches an ATA 00904 * Controller to Native Mode" in the Storage section of the 00905 * Windows Driver Kit for more details. 00906 */ 00907 PdoExtension->IDEInNativeMode = 00908 PciConfigureIdeController(PdoExtension, PciData, TRUE); 00909 } 00910 00911 /* Is native mode enabled after all? */ 00912 if ((PciData->ProgIf & 5) != 5) 00913 { 00914 /* Compatible mode, so force ISA-style IRQ14 and IRQ 15 */ 00915 PciData->u.type0.InterruptPin = 0; 00916 } 00917 } 00918 00919 /* Is this a PCI device with legacy VGA card decodes on the root bus? */ 00920 if ((PdoExtension->HackFlags & PCI_HACK_VIDEO_LEGACY_DECODE) && 00921 (PCI_IS_ROOT_FDO(DeviceExtension)) && 00922 !(DeviceExtension->BrokenVideoHackApplied)) 00923 { 00924 /* Tell the arbiter to apply a hack for these older devices */ 00925 ario_ApplyBrokenVideoHack(DeviceExtension); 00926 } 00927 00928 /* Is this a Compaq PCI Hotplug Controller (r17) on a PAE system ? */ 00929 if ((PciData->VendorID == 0xE11) && 00930 (PciData->DeviceID == 0xA0F7) && 00931 (PciData->RevisionID == 17) && 00932 (ExIsProcessorFeaturePresent(PF_PAE_ENABLED))) 00933 { 00934 /* Turn off the decodes immediately */ 00935 PciData->Command &= ~(PCI_ENABLE_IO_SPACE | 00936 PCI_ENABLE_MEMORY_SPACE | 00937 PCI_ENABLE_BUS_MASTER); 00938 PciWriteDeviceConfig(PdoExtension, 00939 &PciData->Command, 00940 FIELD_OFFSET(PCI_COMMON_HEADER, Command), 00941 sizeof(USHORT)); 00942 00943 /* Do not EVER turn them on again, this will blow up the system */ 00944 PdoExtension->CommandEnables &= ~(PCI_ENABLE_IO_SPACE | 00945 PCI_ENABLE_MEMORY_SPACE | 00946 PCI_ENABLE_BUS_MASTER); 00947 PdoExtension->HackFlags |= PCI_HACK_PRESERVE_COMMAND; 00948 } 00949 break; 00950 00951 /* 00952 * This is called whenever resources are changed and hardware needs to be 00953 * updated. It is concerned with two highly specific erratas on an IBM 00954 * hot-plug docking bridge used on the Thinkpad 600 Series and on Intel's 00955 * ICH PCI Bridges. 00956 */ 00957 case PCI_HACK_FIXUP_BEFORE_UPDATE: 00958 00959 /* There should always be a PDO extension passed in */ 00960 ASSERT(PdoExtension); 00961 00962 /* Is this an IBM 20H2999 PCI Docking Bridge, used on Thinkpads? */ 00963 if ((PdoExtension->VendorId == 0x1014) && 00964 (PdoExtension->DeviceId == 0x95)) 00965 { 00966 /* Read the current command */ 00967 PciReadDeviceConfig(PdoExtension, 00968 &Command, 00969 FIELD_OFFSET(PCI_COMMON_HEADER, Command), 00970 sizeof(USHORT)); 00971 00972 /* Turn off the decodes */ 00973 PciDecodeEnable(PdoExtension, FALSE, &Command); 00974 00975 /* Apply the required IBM workaround */ 00976 PciReadDeviceConfig(PdoExtension, &RegValue, 0xE0, sizeof(UCHAR)); 00977 RegValue &= ~2; 00978 RegValue |= 1; 00979 PciWriteDeviceConfig(PdoExtension, &RegValue, 0xE0, sizeof(UCHAR)); 00980 00981 /* Restore the command to its original value */ 00982 PciWriteDeviceConfig(PdoExtension, 00983 &Command, 00984 FIELD_OFFSET(PCI_COMMON_HEADER, Command), 00985 sizeof(USHORT)); 00986 00987 } 00988 00989 /* 00990 * Check for Intel ICH PCI-to-PCI (i82801) bridges (used on the i810, 00991 * i820, i840, i845 Chipsets) that have subtractive decode enabled, 00992 * and whose hack flags do not specifiy that this support is broken. 00993 */ 00994 if ((PdoExtension->HeaderType == PCI_BRIDGE_TYPE) && 00995 (PdoExtension->Dependent.type1.SubtractiveDecode) && 00996 ((PdoExtension->VendorId == 0x8086) && 00997 ((PdoExtension->DeviceId == 0x2418) || 00998 (PdoExtension->DeviceId == 0x2428) || 00999 (PdoExtension->DeviceId == 0x244E) || 01000 (PdoExtension->DeviceId == 0x2448))) && 01001 !(PdoExtension->HackFlags & PCI_HACK_BROKEN_SUBTRACTIVE_DECODE)) 01002 { 01003 /* 01004 * The positive decode window shouldn't be used, these values are 01005 * normally all read-only or initialized to 0 by the BIOS, but 01006 * it appears Intel doesn't do this, so the PCI Bus Driver will 01007 * do it in software instead. Note that this is used to prevent 01008 * certain non-compliant PCI devices from breaking down due to the 01009 * fact that these ICH bridges have a known "quirk" (which Intel 01010 * documents as a known "erratum", although it's not not really 01011 * an ICH bug since the PCI specification does allow for it) in 01012 * that they will sometimes send non-zero addresses during special 01013 * cycles (ie: non-zero data during the address phase). These 01014 * broken PCI cards will mistakenly attempt to claim the special 01015 * cycle and corrupt their I/O and RAM ranges. Again, in Intel's 01016 * defense, the PCI specification only requires stable data, not 01017 * necessarily zero data, during the address phase. 01018 */ 01019 PciData->u.type1.MemoryBase = 0xFFFF; 01020 PciData->u.type1.PrefetchBase = 0xFFFF; 01021 PciData->u.type1.IOBase = 0xFF; 01022 PciData->u.type1.IOLimit = 0; 01023 PciData->u.type1.MemoryLimit = 0; 01024 PciData->u.type1.PrefetchLimit = 0; 01025 PciData->u.type1.PrefetchBaseUpper32 = 0; 01026 PciData->u.type1.PrefetchLimitUpper32 = 0; 01027 PciData->u.type1.IOBaseUpper16 = 0; 01028 PciData->u.type1.IOLimitUpper16 = 0; 01029 } 01030 break; 01031 01032 default: 01033 return; 01034 } 01035 01036 /* Finally, also check if this is this a CardBUS device? */ 01037 if (PCI_CONFIGURATION_TYPE(PciData) == PCI_CARDBUS_BRIDGE_TYPE) 01038 { 01039 /* 01040 * At offset 44h the LegacyBaseAddress is stored, which is cleared by 01041 * ACPI-aware versions of Windows, to disable legacy-mode I/O access to 01042 * CardBus controllers. For more information, see "Supporting CardBus 01043 * Controllers under ACPI" in the "CardBus Controllers and Windows" 01044 * Whitepaper on WHDC. 01045 */ 01046 LegacyBaseAddress = 0; 01047 PciWriteDeviceConfig(PdoExtension, 01048 &LegacyBaseAddress, 01049 sizeof(PCI_COMMON_HEADER) + sizeof(ULONG), 01050 sizeof(ULONG)); 01051 } 01052 } 01053 01054 BOOLEAN 01055 NTAPI 01056 PcipIsSameDevice(IN PPCI_PDO_EXTENSION DeviceExtension, 01057 IN PPCI_COMMON_HEADER PciData) 01058 { 01059 BOOLEAN IdMatch, RevMatch, SubsysMatch; 01060 ULONGLONG HackFlags = DeviceExtension->HackFlags; 01061 01062 /* Check if the IDs match */ 01063 IdMatch = (PciData->VendorID == DeviceExtension->VendorId) && 01064 (PciData->DeviceID == DeviceExtension->DeviceId); 01065 if (!IdMatch) return FALSE; 01066 01067 /* If the device has a valid revision, check if it matches */ 01068 RevMatch = (HackFlags & PCI_HACK_NO_REVISION_AFTER_D3) || 01069 (PciData->RevisionID == DeviceExtension->RevisionId); 01070 if (!RevMatch) return FALSE; 01071 01072 /* For multifunction devices, this is enough to assume they're the same */ 01073 if (PCI_MULTIFUNCTION_DEVICE(PciData)) return TRUE; 01074 01075 /* For bridge devices, there's also nothing else that can be checked */ 01076 if (DeviceExtension->BaseClass == PCI_CLASS_BRIDGE_DEV) return TRUE; 01077 01078 /* Devices, on the other hand, have subsystem data that can be compared */ 01079 SubsysMatch = (HackFlags & (PCI_HACK_NO_SUBSYSTEM | 01080 PCI_HACK_NO_SUBSYSTEM_AFTER_D3)) || 01081 ((DeviceExtension->SubsystemVendorId == 01082 PciData->u.type0.SubVendorID) && 01083 (DeviceExtension->SubsystemId == 01084 PciData->u.type0.SubSystemID)); 01085 return SubsysMatch; 01086 } 01087 01088 BOOLEAN 01089 NTAPI 01090 PciSkipThisFunction(IN PPCI_COMMON_HEADER PciData, 01091 IN PCI_SLOT_NUMBER Slot, 01092 IN UCHAR OperationType, 01093 IN ULONGLONG HackFlags) 01094 { 01095 do 01096 { 01097 /* Check if this is device enumeration */ 01098 if (OperationType == PCI_SKIP_DEVICE_ENUMERATION) 01099 { 01100 /* Check if there's a hackflag saying not to enumerate this device */ 01101 if (HackFlags & PCI_HACK_NO_ENUM_AT_ALL) break; 01102 01103 /* Check if this is the high end of a double decker device */ 01104 if ((HackFlags & PCI_HACK_DOUBLE_DECKER) && 01105 (Slot.u.bits.DeviceNumber >= 16)) 01106 { 01107 /* It belongs to the same device, so skip it */ 01108 DPRINT1(" Device (Ven %04x Dev %04x (d=0x%x, f=0x%x)) is a ghost.\n", 01109 PciData->VendorID, 01110 PciData->DeviceID, 01111 Slot.u.bits.DeviceNumber, 01112 Slot.u.bits.FunctionNumber); 01113 break; 01114 } 01115 } 01116 else if (OperationType == PCI_SKIP_RESOURCE_ENUMERATION) 01117 { 01118 /* Resource enumeration, check for a hackflag saying not to do it */ 01119 if (HackFlags & PCI_HACK_ENUM_NO_RESOURCE) break; 01120 } 01121 else 01122 { 01123 /* Logic error in the driver */ 01124 ASSERTMSG(FALSE, "PCI Skip Function - Operation type unknown."); 01125 } 01126 01127 /* Check for legacy bridges during resource enumeration */ 01128 if ((PciData->BaseClass == PCI_CLASS_BRIDGE_DEV) && 01129 (PciData->SubClass <= PCI_SUBCLASS_BR_MCA) && 01130 (OperationType == PCI_SKIP_RESOURCE_ENUMERATION)) 01131 { 01132 /* Their resources are not enumerated, only PCI and Cardbus/PCMCIA */ 01133 break; 01134 } 01135 else if (PciData->BaseClass == PCI_CLASS_NOT_DEFINED) 01136 { 01137 /* Undefined base class (usually a PCI BIOS/ROM bug) */ 01138 DPRINT1(" Vendor %04x, Device %04x has class code of PCI_CLASS_NOT_DEFINED\n", 01139 PciData->VendorID, 01140 PciData->DeviceID); 01141 01142 /* 01143 * The Alder has an Intel Extended Express System Support Controller 01144 * which presents apparently spurious BARs. When the PCI resource 01145 * code tries to reassign these BARs, the second IO-APIC gets 01146 * disabled (with disastrous consequences). The first BAR is the 01147 * actual IO-APIC, the remaining five bars seem to be spurious 01148 * resources, so ignore this device completely. 01149 */ 01150 if ((PciData->VendorID == 0x8086) && (PciData->DeviceID == 8)) break; 01151 } 01152 01153 /* Other normal PCI cards and bridges are enumerated */ 01154 if (PCI_CONFIGURATION_TYPE(PciData) <= PCI_CARDBUS_BRIDGE_TYPE) return FALSE; 01155 } while (FALSE); 01156 01157 /* Hit one of the known bugs/hackflags, or this is a new kind of PCI unit */ 01158 DPRINT1(" Device skipped (not enumerated).\n"); 01159 return TRUE; 01160 } 01161 01162 VOID 01163 NTAPI 01164 PciGetEnhancedCapabilities(IN PPCI_PDO_EXTENSION PdoExtension, 01165 IN PPCI_COMMON_HEADER PciData) 01166 { 01167 ULONG HeaderType, CapPtr, TargetAgpCapabilityId; 01168 DEVICE_POWER_STATE WakeLevel; 01169 PCI_CAPABILITIES_HEADER AgpCapability; 01170 PCI_PM_CAPABILITY PowerCapabilities; 01171 PAGED_CODE(); 01172 01173 /* Assume no known wake level */ 01174 PdoExtension->PowerState.DeviceWakeLevel = PowerDeviceUnspecified; 01175 01176 /* Make sure the device has capabilities */ 01177 if (!(PciData->Status & PCI_STATUS_CAPABILITIES_LIST)) 01178 { 01179 /* If it doesn't, there will be no power management */ 01180 PdoExtension->CapabilitiesPtr = 0; 01181 PdoExtension->HackFlags |= PCI_HACK_NO_PM_CAPS; 01182 } 01183 else 01184 { 01185 /* There's capabilities, need to figure out where to get the offset */ 01186 HeaderType = PCI_CONFIGURATION_TYPE(PciData); 01187 if (HeaderType == PCI_CARDBUS_BRIDGE_TYPE) 01188 { 01189 /* Use the bridge's header */ 01190 CapPtr = PciData->u.type2.CapabilitiesPtr; 01191 } 01192 else 01193 { 01194 /* Use the device header */ 01195 ASSERT(HeaderType <= PCI_CARDBUS_BRIDGE_TYPE); 01196 CapPtr = PciData->u.type0.CapabilitiesPtr; 01197 } 01198 01199 /* Skip garbage capabilities pointer */ 01200 if (((CapPtr & 0x3) != 0) || (CapPtr < PCI_COMMON_HDR_LENGTH)) 01201 { 01202 /* Report no extended capabilities */ 01203 PdoExtension->CapabilitiesPtr = 0; 01204 PdoExtension->HackFlags |= PCI_HACK_NO_PM_CAPS; 01205 } 01206 else 01207 { 01208 DPRINT1("Device has capabilities at: %lx\n", CapPtr); 01209 PdoExtension->CapabilitiesPtr = CapPtr; 01210 01211 /* Check for PCI-to-PCI Bridges and AGP bridges */ 01212 if ((PdoExtension->BaseClass == PCI_CLASS_BRIDGE_DEV) && 01213 ((PdoExtension->SubClass == PCI_SUBCLASS_BR_HOST) || 01214 (PdoExtension->SubClass == PCI_SUBCLASS_BR_PCI_TO_PCI))) 01215 { 01216 /* Query either the raw AGP capabilitity, or the Target AGP one */ 01217 TargetAgpCapabilityId = (PdoExtension->SubClass == 01218 PCI_SUBCLASS_BR_PCI_TO_PCI) ? 01219 PCI_CAPABILITY_ID_AGP_TARGET : 01220 PCI_CAPABILITY_ID_AGP; 01221 if (PciReadDeviceCapability(PdoExtension, 01222 PdoExtension->CapabilitiesPtr, 01223 TargetAgpCapabilityId, 01224 &AgpCapability, 01225 sizeof(PCI_CAPABILITIES_HEADER))) 01226 { 01227 /* AGP target ID was found, store it */ 01228 DPRINT1("AGP ID: %lx\n", TargetAgpCapabilityId); 01229 PdoExtension->TargetAgpCapabilityId = TargetAgpCapabilityId; 01230 } 01231 } 01232 01233 /* Check for devices that are known not to have proper power management */ 01234 if (!(PdoExtension->HackFlags & PCI_HACK_NO_PM_CAPS)) 01235 { 01236 /* Query if this device supports power management */ 01237 if (!PciReadDeviceCapability(PdoExtension, 01238 PdoExtension->CapabilitiesPtr, 01239 PCI_CAPABILITY_ID_POWER_MANAGEMENT, 01240 &PowerCapabilities.Header, 01241 sizeof(PCI_PM_CAPABILITY))) 01242 { 01243 /* No power management, so act as if it had the hackflag set */ 01244 DPRINT1("No PM caps, disabling PM\n"); 01245 PdoExtension->HackFlags |= PCI_HACK_NO_PM_CAPS; 01246 } 01247 else 01248 { 01249 /* Otherwise, pick the highest wake level that is supported */ 01250 WakeLevel = PowerDeviceUnspecified; 01251 if (PowerCapabilities.PMC.Capabilities.Support.PMED0) 01252 WakeLevel = PowerDeviceD0; 01253 if (PowerCapabilities.PMC.Capabilities.Support.PMED1) 01254 WakeLevel = PowerDeviceD1; 01255 if (PowerCapabilities.PMC.Capabilities.Support.PMED2) 01256 WakeLevel = PowerDeviceD2; 01257 if (PowerCapabilities.PMC.Capabilities.Support.PMED3Hot) 01258 WakeLevel = PowerDeviceD3; 01259 if (PowerCapabilities.PMC.Capabilities.Support.PMED3Cold) 01260 WakeLevel = PowerDeviceD3; 01261 PdoExtension->PowerState.DeviceWakeLevel = WakeLevel; 01262 01263 /* Convert the PCI power state to the NT power state */ 01264 PdoExtension->PowerState.CurrentDeviceState = 01265 PowerCapabilities.PMCSR.ControlStatus.PowerState + 1; 01266 01267 /* Save all the power capabilities */ 01268 PdoExtension->PowerCapabilities = PowerCapabilities.PMC.Capabilities; 01269 DPRINT1("PM Caps Found! Wake Level: %d Power State: %d\n", 01270 WakeLevel, PdoExtension->PowerState.CurrentDeviceState); 01271 } 01272 } 01273 } 01274 } 01275 01276 /* At the very end of all this, does this device not have power management? */ 01277 if (PdoExtension->HackFlags & PCI_HACK_NO_PM_CAPS) 01278 { 01279 /* Then guess the current state based on whether the decodes are on */ 01280 PdoExtension->PowerState.CurrentDeviceState = 01281 PciData->Command & (PCI_ENABLE_IO_SPACE | 01282 PCI_ENABLE_MEMORY_SPACE | 01283 PCI_ENABLE_BUS_MASTER) ? 01284 PowerDeviceD0: PowerDeviceD3; 01285 DPRINT1("PM is off, so assumed device is: %d based on enables\n", 01286 PdoExtension->PowerState.CurrentDeviceState); 01287 } 01288 } 01289 01290 VOID 01291 NTAPI 01292 PciWriteLimitsAndRestoreCurrent(IN PVOID Reserved, 01293 IN PVOID Context2) 01294 { 01295 PPCI_CONFIGURATOR_CONTEXT Context = Context2; 01296 PPCI_COMMON_HEADER PciData, Current; 01297 PPCI_PDO_EXTENSION PdoExtension; 01298 01299 /* Grab all parameters from the context */ 01300 PdoExtension = Context->PdoExtension; 01301 Current = Context->Current; 01302 PciData = Context->PciData; 01303 01304 /* Write the limit discovery header */ 01305 PciWriteDeviceConfig(PdoExtension, PciData, 0, PCI_COMMON_HDR_LENGTH); 01306 01307 /* Now read what the device indicated the limits are */ 01308 PciReadDeviceConfig(PdoExtension, PciData, 0, PCI_COMMON_HDR_LENGTH); 01309 01310 /* Then write back the original configuration header */ 01311 PciWriteDeviceConfig(PdoExtension, Current, 0, PCI_COMMON_HDR_LENGTH); 01312 01313 /* Copy back the original command that was saved in the context */ 01314 Current->Command = Context->Command; 01315 if (Context->Command) 01316 { 01317 /* Program it back into the device */ 01318 PciWriteDeviceConfig(PdoExtension, 01319 &Context->Command, 01320 FIELD_OFFSET(PCI_COMMON_HEADER, Command), 01321 sizeof(USHORT)); 01322 } 01323 01324 /* Copy back the original status that was saved as well */ 01325 Current->Status = Context->Status; 01326 01327 /* Call the configurator to restore any other data that might've changed */ 01328 Context->Configurator->RestoreCurrent(Context); 01329 } 01330 01331 NTSTATUS 01332 NTAPI 01333 PcipGetFunctionLimits(IN PPCI_CONFIGURATOR_CONTEXT Context) 01334 { 01335 PPCI_CONFIGURATOR Configurator; 01336 PPCI_COMMON_HEADER PciData, Current; 01337 PPCI_PDO_EXTENSION PdoExtension; 01338 PCI_IPI_CONTEXT IpiContext; 01339 PIO_RESOURCE_DESCRIPTOR IoDescriptor; 01340 ULONG Offset; 01341 PAGED_CODE(); 01342 01343 /* Grab all parameters from the context */ 01344 PdoExtension = Context->PdoExtension; 01345 Current = Context->Current; 01346 PciData = Context->PciData; 01347 01348 /* Save the current PCI Command and Status word */ 01349 Context->Status = Current->Status; 01350 Context->Command = Current->Command; 01351 01352 /* Now that they're saved, clear the status, and disable all decodes */ 01353 Current->Status = 0; 01354 Current->Command &= ~(PCI_ENABLE_IO_SPACE | 01355 PCI_ENABLE_MEMORY_SPACE | 01356 PCI_ENABLE_BUS_MASTER); 01357 01358 /* Make a copy of the current PCI configuration header (with decodes off) */ 01359 RtlCopyMemory(PciData, Current, PCI_COMMON_HDR_LENGTH); 01360 01361 /* Locate the correct resource configurator for this type of device */ 01362 Configurator = &PciConfigurators[PdoExtension->HeaderType]; 01363 Context->Configurator = Configurator; 01364 01365 /* Initialize it, which will typically setup the BARs for limit discovery */ 01366 Configurator->Initialize(Context); 01367 01368 /* Check for critical devices and PCI Debugging devices */ 01369 if ((PdoExtension->HackFlags & PCI_HACK_CRITICAL_DEVICE) || 01370 (PdoExtension->OnDebugPath)) 01371 { 01372 /* Specifically check for a PCI Debugging device */ 01373 if (PdoExtension->OnDebugPath) 01374 { 01375 /* Was it enabled for bus mastering? */ 01376 if (Context->Command & PCI_ENABLE_BUS_MASTER) 01377 { 01378 /* This decode needs to be re-enabled so debugging can work */ 01379 PciData->Command |= PCI_ENABLE_BUS_MASTER; 01380 Current->Command |= PCI_ENABLE_BUS_MASTER; 01381 } 01382 01383 /* Disable the debugger while the discovery is happening */ 01384 KdDisableDebugger(); 01385 } 01386 01387 /* For these devices, an IPI must be sent to force high-IRQL discovery */ 01388 IpiContext.Barrier = 1; 01389 IpiContext.RunCount = 1; 01390 IpiContext.DeviceExtension = PdoExtension; 01391 IpiContext.Function = PciWriteLimitsAndRestoreCurrent; 01392 IpiContext.Context = Context; 01393 KeIpiGenericCall(PciExecuteCriticalSystemRoutine, (ULONG_PTR)&IpiContext); 01394 01395 /* Re-enable the debugger if this was a PCI Debugging Device */ 01396 if (PdoExtension->OnDebugPath) KdEnableDebugger(); 01397 } 01398 else 01399 { 01400 /* Otherwise, it's safe to do this in-line at low IRQL */ 01401 PciWriteLimitsAndRestoreCurrent(PdoExtension, Context); 01402 } 01403 01404 /* 01405 * Check if it's valid to compare the headers to see if limit discovery mode 01406 * has properly exited (the expected case is that the PCI header would now 01407 * be equal to what it was before). In some cases, it is known that this will 01408 * fail, because during PciApplyHacks (among other places), software hacks 01409 * had to be applied to the header, which the hardware-side will not see, and 01410 * thus the headers would appear "different". 01411 */ 01412 if (!PdoExtension->ExpectedWritebackFailure) 01413 { 01414 /* Read the current PCI header now, after discovery has completed */ 01415 PciReadDeviceConfig(PdoExtension, PciData + 1, 0, PCI_COMMON_HDR_LENGTH); 01416 01417 /* Check if the current header at entry, is equal to the header now */ 01418 Offset = RtlCompareMemory(PciData + 1, Current, PCI_COMMON_HDR_LENGTH); 01419 if (Offset != PCI_COMMON_HDR_LENGTH) 01420 { 01421 /* It's not, which means configuration somehow changed, dump this */ 01422 DPRINT1("PCI - CFG space write verify failed at offset 0x%x\n", Offset); 01423 PciDebugDumpCommonConfig(PciData + 1); 01424 DPRINT1("----------\n"); 01425 PciDebugDumpCommonConfig(Current); 01426 } 01427 } 01428 01429 /* This PDO should not already have resources, since this is only done once */ 01430 ASSERT(PdoExtension->Resources == NULL); 01431 01432 /* Allocate the structure that will hold the discovered resources and limits */ 01433 PdoExtension->Resources = ExAllocatePoolWithTag(NonPagedPool, 01434 sizeof(PCI_FUNCTION_RESOURCES), 01435 'BicP'); 01436 if (!PdoExtension->Resources) return STATUS_INSUFFICIENT_RESOURCES; 01437 01438 /* Clear it out for now */ 01439 RtlZeroMemory(PdoExtension->Resources, sizeof(PCI_FUNCTION_RESOURCES)); 01440 01441 /* Now call the configurator, which will first store the limits... */ 01442 Configurator->SaveLimits(Context); 01443 01444 /* ...and then store the current resources being used */ 01445 Configurator->SaveCurrentSettings(Context); 01446 01447 /* Loop all the limit descriptors backwards */ 01448 IoDescriptor = &PdoExtension->Resources->Limit[PCI_TYPE0_ADDRESSES + 1]; 01449 while (TRUE) 01450 { 01451 /* Keep going until a non-null descriptor is found */ 01452 IoDescriptor--; 01453 if (IoDescriptor->Type != CmResourceTypeNull) break; 01454 01455 /* This is a null descriptor, is it the last one? */ 01456 if (IoDescriptor == &PdoExtension->Resources->Limit[PCI_TYPE0_ADDRESSES + 1]) 01457 { 01458 /* This means the descriptor is NULL, which means discovery failed */ 01459 DPRINT1("PCI Resources fail!\n"); 01460 01461 /* No resources will be assigned for the device */ 01462 ExFreePoolWithTag(PdoExtension->Resources, 0); 01463 PdoExtension->Resources = NULL; 01464 break; 01465 } 01466 } 01467 01468 /* Return success here, even if the device has no assigned resources */ 01469 return STATUS_SUCCESS; 01470 } 01471 01472 NTSTATUS 01473 NTAPI 01474 PciGetFunctionLimits(IN PPCI_PDO_EXTENSION PdoExtension, 01475 IN PPCI_COMMON_HEADER Current, 01476 IN ULONGLONG HackFlags) 01477 { 01478 NTSTATUS Status; 01479 PPCI_COMMON_HEADER PciData; 01480 PCI_CONFIGURATOR_CONTEXT Context; 01481 PAGED_CODE(); 01482 01483 /* Do the hackflags indicate this device should be skipped? */ 01484 if (PciSkipThisFunction(Current, 01485 PdoExtension->Slot, 01486 PCI_SKIP_RESOURCE_ENUMERATION, 01487 HackFlags)) 01488 { 01489 /* Do not process its resources */ 01490 return STATUS_SUCCESS; 01491 } 01492 01493 /* Allocate a buffer to hold two PCI configuration headers */ 01494 PciData = ExAllocatePoolWithTag(0, 2 * PCI_COMMON_HDR_LENGTH, 'BicP'); 01495 if (!PciData) return STATUS_INSUFFICIENT_RESOURCES; 01496 01497 /* Set up the context for the resource enumeration, and do it */ 01498 Context.Current = Current; 01499 Context.PciData = PciData; 01500 Context.PdoExtension = PdoExtension; 01501 Status = PcipGetFunctionLimits(&Context); 01502 01503 /* Enumeration is completed, free the PCI headers and return the status */ 01504 ExFreePoolWithTag(PciData, 0); 01505 return Status; 01506 } 01507 01508 VOID 01509 NTAPI 01510 PciProcessBus(IN PPCI_FDO_EXTENSION DeviceExtension) 01511 { 01512 PPCI_PDO_EXTENSION PdoExtension; 01513 PDEVICE_OBJECT PhysicalDeviceObject; 01514 PAGED_CODE(); 01515 01516 /* Get the PDO Extension */ 01517 PhysicalDeviceObject = DeviceExtension->PhysicalDeviceObject; 01518 PdoExtension = (PPCI_PDO_EXTENSION)PhysicalDeviceObject->DeviceExtension; 01519 01520 /* Cheeck if this is the root bus */ 01521 if (!PCI_IS_ROOT_FDO(DeviceExtension)) 01522 { 01523 /* Not really handling this year */ 01524 UNIMPLEMENTED; 01525 while (TRUE); 01526 01527 /* Check for PCI bridges with the ISA bit set, or required */ 01528 if ((PdoExtension) && 01529 (PciClassifyDeviceType(PdoExtension) == PciTypePciBridge) && 01530 ((PdoExtension->Dependent.type1.IsaBitRequired) || 01531 (PdoExtension->Dependent.type1.IsaBitSet))) 01532 { 01533 /* We'll need to do some legacy support */ 01534 UNIMPLEMENTED; 01535 while (TRUE); 01536 } 01537 } 01538 else 01539 { 01540 /* Scan all of the root bus' children bridges */ 01541 for (PdoExtension = DeviceExtension->ChildBridgePdoList; 01542 PdoExtension; 01543 PdoExtension = PdoExtension->NextBridge) 01544 { 01545 /* Find any that have the VGA decode bit on */ 01546 if (PdoExtension->Dependent.type1.VgaBitSet) 01547 { 01548 /* Again, some more legacy support we'll have to do */ 01549 UNIMPLEMENTED; 01550 while (TRUE); 01551 } 01552 } 01553 } 01554 01555 /* Check for ACPI systems where the OS assigns bus numbers */ 01556 if (PciAssignBusNumbers) 01557 { 01558 /* Not yet supported */ 01559 UNIMPLEMENTED; 01560 while (TRUE); 01561 } 01562 } 01563 01564 NTSTATUS 01565 NTAPI 01566 PciScanBus(IN PPCI_FDO_EXTENSION DeviceExtension) 01567 { 01568 ULONG MaxDevice = PCI_MAX_DEVICES; 01569 BOOLEAN ProcessFlag = FALSE; 01570 ULONG i, j, k, Size; 01571 USHORT CapOffset, TempOffset; 01572 LONGLONG HackFlags; 01573 PDEVICE_OBJECT DeviceObject; 01574 UCHAR Buffer[PCI_COMMON_HDR_LENGTH]; 01575 UCHAR BiosBuffer[PCI_COMMON_HDR_LENGTH]; 01576 PPCI_COMMON_HEADER PciData = (PVOID)Buffer; 01577 PPCI_COMMON_HEADER BiosData = (PVOID)BiosBuffer; 01578 PCI_SLOT_NUMBER PciSlot; 01579 PCHAR Name; 01580 NTSTATUS Status; 01581 PPCI_PDO_EXTENSION PdoExtension, NewExtension; 01582 PPCI_PDO_EXTENSION* BridgeExtension; 01583 PWCHAR DescriptionText; 01584 USHORT SubVendorId, SubSystemId; 01585 PCI_CAPABILITIES_HEADER CapHeader, PcixCapHeader; 01586 UCHAR SecondaryBus; 01587 DPRINT1("PCI Scan Bus: FDO Extension @ 0x%x, Base Bus = 0x%x\n", 01588 DeviceExtension, DeviceExtension->BaseBus); 01589 01590 /* Is this the root FDO? */ 01591 if (!PCI_IS_ROOT_FDO(DeviceExtension)) 01592 { 01593 /* Get the PDO for the child bus */ 01594 PdoExtension = DeviceExtension->PhysicalDeviceObject->DeviceExtension; 01595 ASSERT_PDO(PdoExtension); 01596 01597 /* Check for hack which only allows bus to have one child device */ 01598 if (PdoExtension->HackFlags & PCI_HACK_ONE_CHILD) MaxDevice = 1; 01599 01600 /* Check if the secondary bus number has changed */ 01601 PciReadDeviceConfig(PdoExtension, 01602 &SecondaryBus, 01603 FIELD_OFFSET(PCI_COMMON_HEADER, u.type1.SecondaryBus), 01604 sizeof(UCHAR)); 01605 if (SecondaryBus != PdoExtension->Dependent.type1.SecondaryBus) 01606 { 01607 DPRINT1("PCI: Bus numbers have been changed! Restoring originals.\n"); 01608 UNIMPLEMENTED; 01609 while (TRUE); 01610 } 01611 } 01612 01613 /* Loop every device on the bus */ 01614 PciSlot.u.bits.Reserved = 0; 01615 i = DeviceExtension->BaseBus; 01616 for (j = 0; j < MaxDevice; j++) 01617 { 01618 /* Loop every function of each device */ 01619 PciSlot.u.bits.DeviceNumber = j; 01620 for (k = 0; k < PCI_MAX_FUNCTION; k++) 01621 { 01622 /* Build the final slot structure */ 01623 PciSlot.u.bits.FunctionNumber = k; 01624 01625 /* Read the vendor for this slot */ 01626 PciReadSlotConfig(DeviceExtension, 01627 PciSlot, 01628 PciData, 01629 0, 01630 sizeof(USHORT)); 01631 01632 /* Skip invalid device */ 01633 if (PciData->VendorID == PCI_INVALID_VENDORID) continue; 01634 01635 /* Now read the whole header */ 01636 PciReadSlotConfig(DeviceExtension, 01637 PciSlot, 01638 &PciData->DeviceID, 01639 sizeof(USHORT), 01640 PCI_COMMON_HDR_LENGTH - sizeof(USHORT)); 01641 01642 /* Apply any hacks before even analyzing the configuration header */ 01643 PciApplyHacks(DeviceExtension, 01644 PciData, 01645 PciSlot, 01646 PCI_HACK_FIXUP_BEFORE_CONFIGURATION, 01647 NULL); 01648 01649 /* Dump device that was found */ 01650 DPRINT1("Scan Found Device 0x%x (b=0x%x, d=0x%x, f=0x%x)\n", 01651 PciSlot.u.AsULONG, 01652 i, 01653 j, 01654 k); 01655 01656 /* Dump the device's header */ 01657 PciDebugDumpCommonConfig(PciData); 01658 01659 /* Find description for this device for the debugger's sake */ 01660 DescriptionText = PciGetDeviceDescriptionMessage(PciData->BaseClass, 01661 PciData->SubClass); 01662 DPRINT1("Device Description \"%S\".\n", 01663 DescriptionText ? DescriptionText : L"(NULL)"); 01664 if (DescriptionText) ExFreePoolWithTag(DescriptionText, 0); 01665 01666 /* Check if there is an ACPI Watchdog Table */ 01667 if (WdTable) 01668 { 01669 /* Check if this PCI device is the ACPI Watchdog Device... */ 01670 UNIMPLEMENTED; 01671 while (TRUE); 01672 } 01673 01674 /* Check for non-simple devices */ 01675 if ((PCI_MULTIFUNCTION_DEVICE(PciData)) || 01676 (PciData->BaseClass == PCI_CLASS_BRIDGE_DEV)) 01677 { 01678 /* No subsystem data defined for these kinds of bridges */ 01679 SubVendorId = 0; 01680 SubSystemId = 0; 01681 } 01682 else 01683 { 01684 /* Read the subsystem information from the PCI header */ 01685 SubVendorId = PciData->u.type0.SubVendorID; 01686 SubSystemId = PciData->u.type0.SubSystemID; 01687 } 01688 01689 /* Get any hack flags for this device */ 01690 HackFlags = PciGetHackFlags(PciData->VendorID, 01691 PciData->DeviceID, 01692 SubVendorId, 01693 SubSystemId, 01694 PciData->RevisionID); 01695 01696 /* Check if this device is considered critical by the OS */ 01697 if (PciIsCriticalDeviceClass(PciData->BaseClass, PciData->SubClass)) 01698 { 01699 /* Check if normally the decodes would be disabled */ 01700 if (!(HackFlags & PCI_HACK_DONT_DISABLE_DECODES)) 01701 { 01702 /* Because this device is critical, don't disable them */ 01703 DPRINT1("Not allowing PM Because device is critical\n"); 01704 HackFlags |= PCI_HACK_CRITICAL_DEVICE; 01705 } 01706 } 01707 01708 /* PCI bridges with a VGA card are also considered critical */ 01709 if ((PciData->BaseClass == PCI_CLASS_BRIDGE_DEV) && 01710 (PciData->SubClass == PCI_SUBCLASS_BR_PCI_TO_PCI) && 01711 (PciData->u.type1.BridgeControl & PCI_ENABLE_BRIDGE_VGA) && 01712 !(HackFlags & PCI_HACK_DONT_DISABLE_DECODES)) 01713 { 01714 /* Do not disable their decodes either */ 01715 DPRINT1("Not allowing PM because device is VGA\n"); 01716 HackFlags |= PCI_HACK_CRITICAL_DEVICE; 01717 } 01718 01719 /* Check if the device should be skipped for whatever reason */ 01720 if (PciSkipThisFunction(PciData, 01721 PciSlot, 01722 PCI_SKIP_DEVICE_ENUMERATION, 01723 HackFlags)) 01724 { 01725 /* Skip this device */ 01726 continue; 01727 } 01728 01729 /* Check if a PDO has already been created for this device */ 01730 PdoExtension = PciFindPdoByFunction(DeviceExtension, 01731 PciSlot.u.AsULONG, 01732 PciData); 01733 if (PdoExtension) 01734 { 01735 /* Rescan scenarios are not yet implemented */ 01736 UNIMPLEMENTED; 01737 while (TRUE); 01738 } 01739 01740 /* Bus processing will need to happen */ 01741 ProcessFlag = TRUE; 01742 01743 /* Create the PDO for this device */ 01744 Status = PciPdoCreate(DeviceExtension, PciSlot, &DeviceObject); 01745 ASSERT(NT_SUCCESS(Status)); 01746 NewExtension = (PPCI_PDO_EXTENSION)DeviceObject->DeviceExtension; 01747 01748 /* Check for broken devices with wrong/no class codes */ 01749 if (HackFlags & PCI_HACK_FAKE_CLASS_CODE) 01750 { 01751 /* Setup a default one */ 01752 PciData->BaseClass = PCI_CLASS_BASE_SYSTEM_DEV; 01753 PciData->SubClass = PCI_SUBCLASS_SYS_OTHER; 01754 01755 /* Device will behave erratically when reading back data */ 01756 NewExtension->ExpectedWritebackFailure = TRUE; 01757 } 01758 01759 /* Clone all the information from the header */ 01760 NewExtension->VendorId = PciData->VendorID; 01761 NewExtension->DeviceId = PciData->DeviceID; 01762 NewExtension->RevisionId = PciData->RevisionID; 01763 NewExtension->ProgIf = PciData->ProgIf; 01764 NewExtension->SubClass = PciData->SubClass; 01765 NewExtension->BaseClass = PciData->BaseClass; 01766 NewExtension->HeaderType = PCI_CONFIGURATION_TYPE(PciData); 01767 01768 /* Check for modern bridge types, which are managed by the driver */ 01769 if ((NewExtension->BaseClass == PCI_CLASS_BRIDGE_DEV) && 01770 ((NewExtension->SubClass == PCI_SUBCLASS_BR_PCI_TO_PCI) || 01771 (NewExtension->SubClass == PCI_SUBCLASS_BR_CARDBUS))) 01772 { 01773 /* Acquire this device's lock */ 01774 KeEnterCriticalRegion(); 01775 KeWaitForSingleObject(&DeviceExtension->ChildListLock, 01776 Executive, 01777 KernelMode, 01778 FALSE, 01779 NULL); 01780 01781 /* Scan the bridge list until the first free entry */ 01782 for (BridgeExtension = &DeviceExtension->ChildBridgePdoList; 01783 *BridgeExtension; 01784 BridgeExtension = &(*BridgeExtension)->NextBridge); 01785 01786 /* Add this PDO as a bridge */ 01787 *BridgeExtension = NewExtension; 01788 ASSERT(NewExtension->NextBridge == NULL); 01789 01790 /* Release this device's lock */ 01791 KeSetEvent(&DeviceExtension->ChildListLock, 01792 IO_NO_INCREMENT, 01793 FALSE); 01794 KeLeaveCriticalRegion(); 01795 } 01796 01797 /* Get the PCI BIOS configuration saved in the registry */ 01798 Status = PciGetBiosConfig(NewExtension, BiosData); 01799 if (NT_SUCCESS(Status)) 01800 { 01801 /* This path has not yet been fully tested by eVb */ 01802 DPRINT1("Have BIOS configuration!\n"); 01803 UNIMPLEMENTED; 01804 01805 /* Check if the PCI BIOS configuration has changed */ 01806 if (!PcipIsSameDevice(NewExtension, BiosData)) 01807 { 01808 /* This is considered failure, and new data will be saved */ 01809 Status = STATUS_UNSUCCESSFUL; 01810 } 01811 else 01812 { 01813 /* Data is still correct, check for interrupt line change */ 01814 if (BiosData->u.type0.InterruptLine != 01815 PciData->u.type0.InterruptLine) 01816 { 01817 /* Update the current BIOS with the saved interrupt line */ 01818 PciWriteDeviceConfig(NewExtension, 01819 &BiosData->u.type0.InterruptLine, 01820 FIELD_OFFSET(PCI_COMMON_HEADER, 01821 u.type0.InterruptLine), 01822 sizeof(UCHAR)); 01823 } 01824 01825 /* Save the BIOS interrupt line and the initial command */ 01826 NewExtension->RawInterruptLine = BiosData->u.type0.InterruptLine; 01827 NewExtension->InitialCommand = BiosData->Command; 01828 } 01829 } 01830 01831 /* Check if no saved data was present or if it was a mismatch */ 01832 if (!NT_SUCCESS(Status)) 01833 { 01834 /* Save the new data */ 01835 Status = PciSaveBiosConfig(NewExtension, PciData); 01836 ASSERT(NT_SUCCESS(Status)); 01837 01838 /* Save the interrupt line and command from the device */ 01839 NewExtension->RawInterruptLine = PciData->u.type0.InterruptLine; 01840 NewExtension->InitialCommand = PciData->Command; 01841 } 01842 01843 /* Save original command from the device and hack flags */ 01844 NewExtension->CommandEnables = PciData->Command; 01845 NewExtension->HackFlags = HackFlags; 01846 01847 /* Get power, AGP, and other capability data */ 01848 PciGetEnhancedCapabilities(NewExtension, PciData); 01849 01850 /* Now configure the BARs */ 01851 Status = PciGetFunctionLimits(NewExtension, PciData, HackFlags); 01852 01853 /* Power up the device */ 01854 PciSetPowerManagedDevicePowerState(NewExtension, PowerDeviceD0, FALSE); 01855 01856 /* Apply any device hacks required for enumeration */ 01857 PciApplyHacks(DeviceExtension, 01858 PciData, 01859 PciSlot, 01860 PCI_HACK_FIXUP_AFTER_CONFIGURATION, 01861 NewExtension); 01862 01863 /* Save interrupt pin */ 01864 NewExtension->InterruptPin = PciData->u.type0.InterruptPin; 01865 01866 /* 01867 * Use either this device's actual IRQ line or, if it's connected on 01868 * a master bus whose IRQ line is actually connected to the host, use 01869 * the HAL to query the bus' IRQ line and store that as the adjusted 01870 * interrupt line instead 01871 */ 01872 NewExtension->AdjustedInterruptLine = PciGetAdjustedInterruptLine(NewExtension); 01873 01874 /* Check if this device is used for PCI debugger cards */ 01875 NewExtension->OnDebugPath = PciIsDeviceOnDebugPath(NewExtension); 01876 01877 /* Check for devices with invalid/bogus subsystem data */ 01878 if (HackFlags & PCI_HACK_NO_SUBSYSTEM) 01879 { 01880 /* Set the subsystem information to zero instead */ 01881 NewExtension->SubsystemVendorId = 0; 01882 NewExtension->SubsystemId = 0; 01883 } 01884 01885 /* Scan all capabilities */ 01886 CapOffset = NewExtension->CapabilitiesPtr; 01887 while (CapOffset) 01888 { 01889 /* Read this header */ 01890 TempOffset = PciReadDeviceCapability(NewExtension, 01891 CapOffset, 01892 0, 01893 &CapHeader, 01894 sizeof(PCI_CAPABILITIES_HEADER)); 01895 if (TempOffset != CapOffset) 01896 { 01897 /* This is a strange issue that shouldn't happen normally */ 01898 DPRINT1("PCI - Failed to read PCI capability at offset 0x%02x\n", 01899 CapOffset); 01900 ASSERT(TempOffset == CapOffset); 01901 } 01902 01903 /* Check for capabilities that this driver cares about */ 01904 switch (CapHeader.CapabilityID) 01905 { 01906 /* Power management capability is heavily used by the bus */ 01907 case PCI_CAPABILITY_ID_POWER_MANAGEMENT: 01908 01909 /* Dump the capability */ 01910 Name = "POWER"; 01911 Size = sizeof(PCI_PM_CAPABILITY); 01912 break; 01913 01914 /* AGP capability is required for AGP bus functionality */ 01915 case PCI_CAPABILITY_ID_AGP: 01916 01917 /* Dump the capability */ 01918 Name = "AGP"; 01919 Size = sizeof(PCI_AGP_CAPABILITY); 01920 break; 01921 01922 /* This driver doesn't really use anything other than that */ 01923 default: 01924 01925 /* Windows prints this, we could do a translation later */ 01926 Name = "UNKNOWN CAPABILITY"; 01927 Size = 0; 01928 break; 01929 } 01930 01931 /* Check if this is a capability that should be dumped */ 01932 if (Size) 01933 { 01934 /* Read the whole capability data */ 01935 TempOffset = PciReadDeviceCapability(NewExtension, 01936 CapOffset, 01937 CapHeader.CapabilityID, 01938 &CapHeader, 01939 Size); 01940 01941 if (TempOffset != CapOffset) 01942 { 01943 /* Again, a strange issue that shouldn't be seen */ 01944 DPRINT1("- Failed to read capability data. ***\n"); 01945 ASSERT(TempOffset == CapOffset); 01946 } 01947 } 01948 01949 /* Dump this capability */ 01950 DPRINT1("CAP @%02x ID %02x (%s)\n", 01951 CapOffset, CapHeader.CapabilityID, Name); 01952 for (i = 0; i < Size; i += 2) 01953 DPRINT1(" %04x\n", *(PUSHORT)((ULONG_PTR)&CapHeader + i)); 01954 DPRINT1("\n"); 01955 01956 /* Check the next capability */ 01957 CapOffset = CapHeader.Next; 01958 } 01959 01960 /* Check for IDE controllers */ 01961 if ((NewExtension->BaseClass == PCI_CLASS_MASS_STORAGE_CTLR) && 01962 (NewExtension->SubClass == PCI_SUBCLASS_MSC_IDE_CTLR)) 01963 { 01964 /* Do not allow them to power down completely */ 01965 NewExtension->DisablePowerDown = TRUE; 01966 } 01967 01968 /* 01969 * Check if this is a legacy bridge. Note that the i82375 PCI/EISA 01970 * bridge that is present on certain NT Alpha machines appears as 01971 * non-classified so detect it manually by scanning for its VID/PID. 01972 */ 01973 if (((NewExtension->BaseClass == PCI_CLASS_BRIDGE_DEV) && 01974 ((NewExtension->SubClass == PCI_SUBCLASS_BR_ISA) || 01975 (NewExtension->SubClass == PCI_SUBCLASS_BR_EISA) || 01976 (NewExtension->SubClass == PCI_SUBCLASS_BR_MCA))) || 01977 ((NewExtension->VendorId == 0x8086) && 01978 (NewExtension->DeviceId == 0x482))) 01979 { 01980 /* Do not allow these legacy bridges to be powered down */ 01981 NewExtension->DisablePowerDown = TRUE; 01982 } 01983 01984 /* Check if the BIOS did not configure a cache line size */ 01985 if (!PciData->CacheLineSize) 01986 { 01987 /* Check if the device is disabled */ 01988 if (!(NewExtension->CommandEnables & (PCI_ENABLE_IO_SPACE | 01989 PCI_ENABLE_MEMORY_SPACE | 01990 PCI_ENABLE_BUS_MASTER))) 01991 { 01992 /* Check if this is a PCI-X device*/ 01993 TempOffset = PciReadDeviceCapability(NewExtension, 01994 NewExtension->CapabilitiesPtr, 01995 PCI_CAPABILITY_ID_PCIX, 01996 &PcixCapHeader, 01997 sizeof(PCI_CAPABILITIES_HEADER)); 01998 01999 /* 02000 * A device with default cache line size and latency timer 02001 * settings is considered to be unconfigured. Note that on 02002 * PCI-X, the reset value of the latency timer field in the 02003 * header is 64, not 0, hence why the check for PCI-X caps 02004 * was required, and the value used here below. 02005 */ 02006 if (!(PciData->LatencyTimer) || 02007 ((TempOffset) && (PciData->LatencyTimer == 64))) 02008 { 02009 /* Keep track of the fact that it needs configuration */ 02010 DPRINT1("PCI - ScanBus, PDOx %x found unconfigured\n", 02011 NewExtension); 02012 NewExtension->NeedsHotPlugConfiguration = TRUE; 02013 } 02014 } 02015 } 02016 02017 /* Save latency and cache size information */ 02018 NewExtension->SavedLatencyTimer = PciData->LatencyTimer; 02019 NewExtension->SavedCacheLineSize = PciData->CacheLineSize; 02020 02021 /* The PDO is now ready to go */ 02022 DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING; 02023 } 02024 } 02025 02026 /* Enumeration completed, do a final pass now that all devices are found */ 02027 if (ProcessFlag) PciProcessBus(DeviceExtension); 02028 return STATUS_SUCCESS; 02029 } 02030 02031 NTSTATUS 02032 NTAPI 02033 PciQueryDeviceRelations(IN PPCI_FDO_EXTENSION DeviceExtension, 02034 IN OUT PDEVICE_RELATIONS *pDeviceRelations) 02035 { 02036 NTSTATUS Status; 02037 PPCI_PDO_EXTENSION PdoExtension; 02038 ULONG PdoCount = 0; 02039 PDEVICE_RELATIONS DeviceRelations, NewRelations; 02040 SIZE_T Size; 02041 PDEVICE_OBJECT DeviceObject, *ObjectArray; 02042 PAGED_CODE(); 02043 02044 /* Make sure the FDO is started */ 02045 ASSERT(DeviceExtension->DeviceState == PciStarted); 02046 02047 /* Synchronize while we enumerate the bus */ 02048 Status = PciBeginStateTransition(DeviceExtension, PciSynchronizedOperation); 02049 if (!NT_SUCCESS(Status)) return Status; 02050 02051 /* Scan all children PDO */ 02052 for (PdoExtension = DeviceExtension->ChildPdoList; 02053 PdoExtension; 02054 PdoExtension = PdoExtension->Next) 02055 { 02056 /* Invalidate them */ 02057 PdoExtension->NotPresent = TRUE; 02058 } 02059 02060 /* Scan the PCI Bus */ 02061 Status = PciScanBus(DeviceExtension); 02062 ASSERT(NT_SUCCESS(Status)); 02063 02064 /* Enumerate all children PDO again */ 02065 for (PdoExtension = DeviceExtension->ChildPdoList; 02066 PdoExtension; 02067 PdoExtension = PdoExtension->Next) 02068 { 02069 /* Check for PDOs that are still invalidated */ 02070 if (PdoExtension->NotPresent) 02071 { 02072 /* This means this PDO existed before, but not anymore */ 02073 PdoExtension->ReportedMissing = TRUE; 02074 DPRINT1("PCI - Old device (pdox) %08x not found on rescan.\n", 02075 PdoExtension); 02076 } 02077 else 02078 { 02079 /* Increase count of detected PDOs */ 02080 PdoCount++; 02081 } 02082 } 02083 02084 /* Read the current relations and add the newly discovered relations */ 02085 DeviceRelations = *pDeviceRelations; 02086 Size = FIELD_OFFSET(DEVICE_RELATIONS, Objects) + 02087 PdoCount * sizeof(PDEVICE_OBJECT); 02088 if (DeviceRelations) Size += sizeof(PDEVICE_OBJECT) * DeviceRelations->Count; 02089 02090 /* Allocate the device relations */ 02091 NewRelations = (PDEVICE_RELATIONS)ExAllocatePoolWithTag(0, Size, 'BicP'); 02092 if (!NewRelations) 02093 { 02094 /* Out of space, cancel the operation */ 02095 PciCancelStateTransition(DeviceExtension, PciSynchronizedOperation); 02096 return STATUS_INSUFFICIENT_RESOURCES; 02097 } 02098 02099 /* Check if there were any older relations */ 02100 NewRelations->Count = 0; 02101 if (DeviceRelations) 02102 { 02103 /* Copy the old relations into the new buffer, then free the old one */ 02104 RtlCopyMemory(NewRelations, 02105 DeviceRelations, 02106 FIELD_OFFSET(DEVICE_RELATIONS, Objects) + 02107 DeviceRelations->Count * sizeof(PDEVICE_OBJECT)); 02108 ExFreePoolWithTag(DeviceRelations, 0); 02109 } 02110 02111 /* Print out that we're ready to dump relations */ 02112 DPRINT1("PCI QueryDeviceRelations/BusRelations FDOx %08x (bus 0x%02x)\n", 02113 DeviceExtension, 02114 DeviceExtension->BaseBus); 02115 02116 /* Loop the current PDO children and the device relation object array */ 02117 PdoExtension = DeviceExtension->ChildPdoList; 02118 ObjectArray = &NewRelations->Objects[NewRelations->Count]; 02119 while (PdoExtension) 02120 { 02121 /* Dump this relation */ 02122 DPRINT1(" QDR PDO %08x (x %08x)%s\n", 02123 PdoExtension->PhysicalDeviceObject, 02124 PdoExtension, 02125 PdoExtension->NotPresent ? 02126 "<Omitted, device flaged not present>" : ""); 02127 02128 /* Is this PDO present? */ 02129 if (!PdoExtension->NotPresent) 02130 { 02131 /* Reference it and add it to the array */ 02132 DeviceObject = PdoExtension->PhysicalDeviceObject; 02133 ObReferenceObject(DeviceObject); 02134 *ObjectArray++ = DeviceObject; 02135 } 02136 02137 /* Go to the next PDO */ 02138 PdoExtension = PdoExtension->Next; 02139 } 02140 02141 /* Terminate dumping the relations */ 02142 DPRINT1(" QDR Total PDO count = %d (%d already in list)\n", 02143 NewRelations->Count + PdoCount, 02144 NewRelations->Count); 02145 02146 /* Return the final count and the new buffer */ 02147 NewRelations->Count += PdoCount; 02148 *pDeviceRelations = NewRelations; 02149 return STATUS_SUCCESS; 02150 } 02151 02152 NTSTATUS 02153 NTAPI 02154 PciSetResources(IN PPCI_PDO_EXTENSION PdoExtension, 02155 IN BOOLEAN DoReset, 02156 IN BOOLEAN SomethingSomethingDarkSide) 02157 { 02158 PPCI_FDO_EXTENSION FdoExtension; 02159 UCHAR NewCacheLineSize, NewLatencyTimer; 02160 PCI_COMMON_HEADER PciData; 02161 BOOLEAN Native; 02162 PPCI_CONFIGURATOR Configurator; 02163 02164 /* Get the FDO and read the configuration data */ 02165 FdoExtension = PdoExtension->ParentFdoExtension; 02166 PciReadDeviceConfig(PdoExtension, &PciData, 0, PCI_COMMON_HDR_LENGTH); 02167 02168 /* Make sure this is still the same device */ 02169 if (!PcipIsSameDevice(PdoExtension, &PciData)) 02170 { 02171 /* Fail */ 02172 ASSERTMSG(FALSE, "PCI Set resources - not same device"); 02173 return STATUS_DEVICE_DOES_NOT_EXIST; 02174 } 02175 02176 /* Nothing to set for a host bridge */ 02177 if ((PdoExtension->BaseClass == PCI_CLASS_BRIDGE_DEV) && 02178 (PdoExtension->SubClass == PCI_SUBCLASS_BR_HOST)) 02179 { 02180 /* Fake success */ 02181 return STATUS_SUCCESS; 02182 } 02183 02184 /* Check if an IDE controller is being reset */ 02185 if ((DoReset) && 02186 (PdoExtension->BaseClass == PCI_CLASS_MASS_STORAGE_CTLR) && 02187 (PdoExtension->SubClass == PCI_SUBCLASS_MSC_IDE_CTLR)) 02188 { 02189 /* Turn off native mode */ 02190 Native = PciConfigureIdeController(PdoExtension, &PciData, FALSE); 02191 ASSERT(Native == PdoExtension->IDEInNativeMode); 02192 } 02193 02194 /* Check for update of a hotplug device, or first configuration of one */ 02195 if ((PdoExtension->NeedsHotPlugConfiguration) && 02196 (FdoExtension->HotPlugParameters.Acquired)) 02197 { 02198 /* Don't have hotplug devices to test with yet, QEMU 0.14 should */ 02199 UNIMPLEMENTED; 02200 while (TRUE); 02201 } 02202 02203 /* Locate the correct resource configurator for this type of device */ 02204 Configurator = &PciConfigurators[PdoExtension->HeaderType]; 02205 02206 /* Apply the settings change */ 02207 Configurator->ChangeResourceSettings(PdoExtension, &PciData); 02208 02209 /* Assume no update needed */ 02210 PdoExtension->UpdateHardware = FALSE; 02211 02212 /* Check if a reset is needed */ 02213 if (DoReset) 02214 { 02215 /* Reset resources */ 02216 Configurator->ResetDevice(PdoExtension, &PciData); 02217 PciData.u.type0.InterruptLine = PdoExtension->RawInterruptLine; 02218 } 02219 02220 /* Check if the latency timer changed */ 02221 NewLatencyTimer = PdoExtension->SavedLatencyTimer; 02222 if (PciData.LatencyTimer != NewLatencyTimer) 02223 { 02224 /* Debug notification */ 02225 DPRINT1("PCI (pdox %08x) changing latency from %02x to %02x.\n", 02226 PdoExtension, 02227 PciData.LatencyTimer, 02228 NewLatencyTimer); 02229 } 02230 02231 /* Check if the cache line changed */ 02232 NewCacheLineSize = PdoExtension->SavedCacheLineSize; 02233 if (PciData.CacheLineSize != NewCacheLineSize) 02234 { 02235 /* Debug notification */ 02236 DPRINT1("PCI (pdox %08x) changing cache line size from %02x to %02x.\n", 02237 PdoExtension, 02238 PciData.CacheLineSize, 02239 NewCacheLineSize); 02240 } 02241 02242 /* Inherit data from PDO extension */ 02243 PciData.LatencyTimer = PdoExtension->SavedLatencyTimer; 02244 PciData.CacheLineSize = PdoExtension->SavedCacheLineSize; 02245 PciData.u.type0.InterruptLine = PdoExtension->RawInterruptLine; 02246 02247 /* Apply any resource hacks required */ 02248 PciApplyHacks(FdoExtension, 02249 &PciData, 02250 PdoExtension->Slot, 02251 PCI_HACK_FIXUP_BEFORE_UPDATE, 02252 PdoExtension); 02253 02254 /* Check if I/O space was disabled by administrator or driver */ 02255 if (PdoExtension->IoSpaceNotRequired) 02256 { 02257 /* Don't turn on the decode */ 02258 PdoExtension->CommandEnables &= ~PCI_ENABLE_IO_SPACE; 02259 } 02260 02261 /* Update the device with the new settings */ 02262 PciUpdateHardware(PdoExtension, &PciData); 02263 02264 /* Update complete */ 02265 PdoExtension->RawInterruptLine = PciData.u.type0.InterruptLine; 02266 PdoExtension->NeedsHotPlugConfiguration = FALSE; 02267 return STATUS_SUCCESS; 02268 } 02269 02270 /* EOF */ Generated on Sat May 26 2012 04:20:15 for ReactOS by
1.7.6.1
|