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