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