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